Merge from vendor branch GROFF:
[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.9 2005/08/09 16:23:13 dillon Exp $
29  */
30
31 #include <dev/raid/ips/ips.h>
32
33 int
34 ips_timed_wait(ips_command_t *command, const char *id, int timo)
35 {
36         int error = 0;
37
38         while (command->completed == 0) {
39                 crit_enter();
40                 if (command->completed == 0)
41                         error = tsleep(&command->completed, 0, id, timo);
42                 crit_exit();
43                 if (error == EWOULDBLOCK) {
44                         error = ETIMEDOUT;
45                         break;
46                 }
47         }
48         return(error);
49 }
50
51 /*
52  * This is an interrupt callback.  It is called from
53  * interrupt context when the adapter has completed the
54  * command, and wakes up anyone waiting on the command.
55  */
56 static void
57 ips_wakeup_callback(ips_command_t *command)
58 {
59         bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
60                         BUS_DMASYNC_POSTWRITE);
61         command->completed = 1;
62         wakeup(&command->completed);
63 }
64
65 /*
66  * Below are a series of functions for sending an IO request
67  * to the adapter.  The flow order is: start, send, callback, finish.
68  * The caller must have already assembled an iorequest struct to hold
69  * the details of the IO request.
70  */
71 static void
72 ips_io_request_finish(ips_command_t *command)
73 {
74         struct bio *iobuf = command->arg;
75
76         if (ips_read_request(iobuf)) {
77                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
78                                 BUS_DMASYNC_POSTREAD);
79         } else {
80                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
81                                 BUS_DMASYNC_POSTWRITE);
82         }
83         bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
84         if (COMMAND_ERROR(&command->status)) {
85                 iobuf->bio_flags |=BIO_ERROR;
86                 iobuf->bio_error = EIO;
87         }
88         ips_insert_free_cmd(command->sc, command);
89         ipsd_finish(iobuf);
90 }
91
92 static void
93 ips_io_request_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
94                         int error)
95 {
96         ips_softc_t *sc;
97         ips_command_t *command = cmdptr;
98         ips_sg_element_t *sg_list;
99         ips_io_cmd *command_struct;
100         struct bio *iobuf = command->arg;
101         int i, length = 0;
102         u_int8_t cmdtype;
103
104         sc = command->sc;
105         if (error) {
106                 printf("ips: error = %d in ips_sg_request_callback\n", error);
107                 bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
108                 iobuf->bio_flags |= BIO_ERROR;
109                 iobuf->bio_error = ENOMEM;
110                 ips_insert_free_cmd(sc, command);
111                 ipsd_finish(iobuf);
112                 return;
113         }
114         command_struct = (ips_io_cmd *)command->command_buffer;
115         command_struct->id = command->id;
116         command_struct->drivenum = (uintptr_t)iobuf->bio_driver1;
117         if (segnum != 1) {
118                 if (ips_read_request(iobuf))
119                         cmdtype = IPS_SG_READ_CMD;
120                 else
121                         cmdtype = IPS_SG_WRITE_CMD;
122                 command_struct->segnum = segnum;
123                 sg_list = (ips_sg_element_t *)((u_int8_t *)
124                            command->command_buffer + IPS_COMMAND_LEN);
125                 for (i = 0; i < segnum; i++) {
126                         sg_list[i].addr = segments[i].ds_addr;
127                         sg_list[i].len = segments[i].ds_len;
128                         length += segments[i].ds_len;
129                 }
130                 command_struct->buffaddr =
131                     (u_int32_t)command->command_phys_addr + IPS_COMMAND_LEN;
132         } else {
133                 if (ips_read_request(iobuf))
134                         cmdtype = IPS_READ_CMD;
135                 else
136                         cmdtype = IPS_WRITE_CMD;
137                 command_struct->buffaddr = segments[0].ds_addr;
138                 length = segments[0].ds_len;
139         }
140         command_struct->command = cmdtype;
141         command_struct->lba = iobuf->bio_pblkno;
142         length = (length + IPS_BLKSIZE - 1)/IPS_BLKSIZE;
143         command_struct->length = length;
144         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
145                         BUS_DMASYNC_PREWRITE);
146         if (ips_read_request(iobuf)) {
147                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
148                                 BUS_DMASYNC_PREREAD);
149         } else {
150                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
151                                 BUS_DMASYNC_PREWRITE);
152         }
153         /*
154          * the cast to long long below is necessary because our b_pblkno
155          * is 32bit wide whereas it's 64bit on FreeBSD-CURRENT.
156          */
157         PRINTF(10, "ips test: command id: %d segments: %d "
158                 "pblkno: %lld length: %d, ds_len: %d\n", command->id, segnum,
159                 (long long)iobuf->bio_pblkno,
160                 length, segments[0].ds_len);
161
162         sc->ips_issue_cmd(command);
163         return;
164 }
165
166 static int
167 ips_send_io_request(ips_command_t *command, struct buf *iobuf)
168 {
169         command->callback = ips_io_request_finish;
170         command->arg = iobuf;
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)
180 {
181         ips_command_t *command;
182         struct bio *iobuf;
183
184         iobuf = bufq_first(&sc->queue);
185         if (iobuf == NULL)
186                 return;
187         if (ips_get_free_cmd(sc, &command, 0) != 0)
188                 return;
189         bufq_remove(&sc->queue, iobuf);
190         ips_send_io_request(command, iobuf);
191 }
192
193 /*
194  * Below are a series of functions for sending an adapter info request
195  * to the adapter.  The flow order is: get, send, callback. It uses
196  * the generic finish callback at the top of this file.
197  * This can be used to get configuration/status info from the card
198  */
199 static void
200 ips_adapter_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum,
201                           int error)
202 {
203         ips_softc_t *sc;
204         ips_command_t *command = cmdptr;
205         ips_adapter_info_cmd *command_struct;
206         sc = command->sc;
207         if (error) {
208                 command->status.value = IPS_ERROR_STATUS; /* a lovely error value */
209                 ips_insert_free_cmd(sc, command);
210                 printf("ips: error = %d in ips_get_adapter_info\n", error);
211                 return;
212         }
213         command_struct = (ips_adapter_info_cmd *)command->command_buffer;
214         command_struct->command = IPS_ADAPTER_INFO_CMD;
215         command_struct->id = command->id;
216         command_struct->buffaddr = segments[0].ds_addr;
217
218         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
219                         BUS_DMASYNC_PREWRITE);
220         bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
221                         BUS_DMASYNC_PREREAD);
222         sc->ips_issue_cmd(command);
223 }
224
225 static int
226 ips_send_adapter_info_cmd(ips_command_t *command)
227 {
228         ips_softc_t *sc = command->sc;
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 ((command->status.value == IPS_ERROR_STATUS) ||
257             ips_timed_wait(command, "ips", 30 * hz) != 0)
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         ips_command_t *command;
279         int error = 0;
280
281         if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
282                 device_printf(sc->dev, "unable to get adapter configuration\n");
283                 return ENXIO;
284         }
285         ips_send_adapter_info_cmd(command);
286         if (COMMAND_ERROR(&command->status))
287                 error = ENXIO;
288         return error;
289 }
290
291 /*
292  * Below are a series of functions for sending a drive info request
293  * to the adapter.  The flow order is: get, send, callback. It uses
294  * the generic finish callback at the top of this file.
295  * This can be used to get drive status info from the card
296  */
297 static void
298 ips_drive_info_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
299                         int error)
300 {
301         ips_softc_t *sc;
302         ips_command_t *command = cmdptr;
303         ips_drive_cmd *command_struct;
304
305         sc = command->sc;
306         if (error) {
307
308                 command->status.value = IPS_ERROR_STATUS;
309                 ips_insert_free_cmd(sc, command);
310                 printf("ips: error = %d in ips_get_drive_info\n", error);
311                 return;
312         }
313         command_struct = (ips_drive_cmd *)command->command_buffer;
314         command_struct->command = IPS_DRIVE_INFO_CMD;
315         command_struct->id = command->id;
316         command_struct->buffaddr = segments[0].ds_addr;
317         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
318             BUS_DMASYNC_PREWRITE);
319         bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
320             BUS_DMASYNC_PREREAD);
321         sc->ips_issue_cmd(command);
322 }
323
324 static int
325 ips_send_drive_info_cmd(ips_command_t *command)
326 {
327         int error = 0;
328         ips_softc_t *sc = command->sc;
329         ips_drive_info_t *driveinfo;
330
331         if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
332                                 /* alignemnt */ 1,
333                                 /* boundary  */ 0,
334                                 /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
335                                 /* highaddr  */ BUS_SPACE_MAXADDR,
336                                 /* filter    */ NULL,
337                                 /* filterarg */ NULL,
338                                 /* maxsize   */ IPS_DRIVE_INFO_LEN,
339                                 /* numsegs   */ 1,
340                                 /* maxsegsize*/ IPS_DRIVE_INFO_LEN,
341                                 /* flags     */ 0,
342                                 &command->data_dmatag) != 0) {
343                 printf("ips: can't alloc dma tag for drive status\n");
344                 error = ENOMEM;
345                 goto exit;
346         }
347         if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
348             BUS_DMA_NOWAIT, &command->data_dmamap)) {
349                 error = ENOMEM;
350                 goto exit;
351         }
352         command->callback = ips_wakeup_callback;
353         bus_dmamap_load(command->data_dmatag, command->data_dmamap,
354             command->data_buffer,IPS_DRIVE_INFO_LEN,
355             ips_drive_info_callback, command, BUS_DMA_NOWAIT);
356         if ((command->status.value == IPS_ERROR_STATUS) ||
357             ips_timed_wait(command, "ips", 10 * hz) != 0)
358                 error = ETIMEDOUT;
359
360         if (error == 0) {
361                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
362                     BUS_DMASYNC_POSTREAD);
363                 driveinfo = command->data_buffer;
364                 memcpy(sc->drives, driveinfo->drives, sizeof(ips_drive_t) * 8);
365                 sc->drivecount = driveinfo->drivecount;
366                 device_printf(sc->dev, "logical drives: %d\n", sc->drivecount);
367         }
368         bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
369 exit:
370         /* I suppose I should clean up my memory allocations */
371         bus_dmamem_free(command->data_dmatag, command->data_buffer,
372                         command->data_dmamap);
373         bus_dma_tag_destroy(command->data_dmatag);
374         ips_insert_free_cmd(sc, command);
375         return error;
376 }
377
378 int
379 ips_get_drive_info(ips_softc_t *sc)
380 {
381         int error = 0;
382         ips_command_t *command;
383
384         if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
385                 device_printf(sc->dev, "unable to get drive configuration\n");
386                 return ENXIO;
387         }
388         ips_send_drive_info_cmd(command);
389         if (COMMAND_ERROR(&command->status))
390                 error = ENXIO;
391         return error;
392 }
393
394 /*
395  * Below is a pair of functions for making sure data is safely
396  * on disk by flushing the adapter's cache.
397  */
398 static int
399 ips_send_flush_cache_cmd(ips_command_t *command)
400 {
401         ips_softc_t *sc = command->sc;
402         ips_generic_cmd *command_struct;
403
404         PRINTF(10,"ips test: got a command, building flush command\n");
405         command->callback = ips_wakeup_callback;
406         command_struct = (ips_generic_cmd *)command->command_buffer;
407         command_struct->command = IPS_CACHE_FLUSH_CMD;
408         command_struct->id = command->id;
409         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
410             BUS_DMASYNC_PREWRITE);
411         sc->ips_issue_cmd(command);
412         if (command->status.value != IPS_ERROR_STATUS)
413                 ips_timed_wait(command, "flush2", 0);
414         ips_insert_free_cmd(sc, command);
415         return 0;
416 }
417
418 int
419 ips_flush_cache(ips_softc_t *sc)
420 {
421         ips_command_t *command;
422
423         device_printf(sc->dev, "flushing cache\n");
424         if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
425                 device_printf(sc->dev, "ERROR: unable to get a command! "
426                     "can't flush cache!\n");
427                 return(1);
428         }
429         ips_send_flush_cache_cmd(command);
430         if (COMMAND_ERROR(&command->status)) {
431                 device_printf(sc->dev, "ERROR: cache flush command failed!\n");
432                 return(1);
433         }
434         return 0;
435 }
436
437 /*
438  * Simplified localtime to provide timevalues for ffdc.
439  * Taken from libc/stdtime/localtime.c
440  */
441 static void
442 ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime)
443 {
444         long    days, rem, y;
445         int     yleap, *ip, month;
446         int     year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR };
447         int     mon_lengths[2][IPS_MONSPERYEAR] = {
448                 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
449                 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
450         };
451
452         days = sctime / IPS_SECSPERDAY;
453         rem  = sctime % IPS_SECSPERDAY;
454
455         command->hour = rem / IPS_SECSPERHOUR;
456         rem           = rem % IPS_SECSPERHOUR;
457
458         command->minute = rem / IPS_SECSPERMIN;
459         command->second = rem % IPS_SECSPERMIN;
460
461         y = IPS_EPOCH_YEAR;
462         while (days < 0 || days >= (long)year_lengths[yleap = ips_isleap(y)]) {
463                 long    newy;
464
465                 newy = y + days / IPS_DAYSPERNYEAR;
466                 if (days < 0)
467                         --newy;
468                 days -= (newy - y) * IPS_DAYSPERNYEAR +
469                     IPS_LEAPS_THRU_END_OF(newy - 1) -
470                     IPS_LEAPS_THRU_END_OF(y - 1);
471                 y = newy;
472         }
473         command->yearH = y / 100;
474         command->yearL = y % 100;
475         ip = mon_lengths[yleap];
476         for (month = 0; days >= (long)ip[month]; ++month)
477                 days = days - (long)ip[month];
478         command->month = month + 1;
479         command->day = days + 1;
480 }
481
482 static int
483 ips_send_ffdc_reset_cmd(ips_command_t *command)
484 {
485         ips_softc_t *sc = command->sc;
486         ips_adapter_ffdc_cmd *command_struct;
487
488         PRINTF(10, "ips test: got a command, building ffdc reset command\n");
489         command->callback = ips_wakeup_callback;
490         command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer;
491         command_struct->command = IPS_FFDC_CMD;
492         command_struct->id = command->id;
493         command_struct->reset_count = sc->ffdc_resetcount;
494         command_struct->reset_type  = 0x0;
495         ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec);
496         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
497             BUS_DMASYNC_PREWRITE);
498         sc->ips_issue_cmd(command);
499         if (command->status.value != IPS_ERROR_STATUS)
500                 ips_timed_wait(command, "ffdc", 0);
501         ips_insert_free_cmd(sc, command);
502         return 0;
503 }
504
505 int
506 ips_ffdc_reset(ips_softc_t *sc)
507 {
508         ips_command_t *command;
509
510         if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
511                 device_printf(sc->dev, "ERROR: unable to get a command! "
512                     "can't send ffdc reset!\n");
513                 return 1;
514         }
515         ips_send_ffdc_reset_cmd(command);
516         if (COMMAND_ERROR(&command->status)) {
517                 device_printf(sc->dev, "ERROR: ffdc reset command failed!\n");
518                 return 1;
519         }
520         return 0;
521 }
522
523 static void
524 ips_write_nvram(ips_command_t *command)
525 {
526         ips_softc_t *sc = command->sc;
527         ips_rw_nvram_cmd *command_struct;
528         ips_nvram_page5 *nvram;
529
530         /*FIXME check for error */
531         command->callback = ips_wakeup_callback;
532         command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
533         command_struct->command = IPS_RW_NVRAM_CMD;
534         command_struct->id = command->id;
535         command_struct->pagenum = 5;
536         command_struct->rw      = 1;    /* write */
537         bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
538             BUS_DMASYNC_POSTREAD);
539         nvram = command->data_buffer;
540         /* retrieve adapter info and save in sc */
541         sc->adapter_type = nvram->adapter_type;
542         strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4);
543         strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4);
544         nvram->operating_system = IPS_OS_FREEBSD;
545         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
546             BUS_DMASYNC_PREWRITE);
547         sc->ips_issue_cmd(command);
548 }
549
550 static void
551 ips_read_nvram_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
552                         int error)
553 {
554         ips_softc_t *sc;
555         ips_command_t *command = cmdptr;
556         ips_rw_nvram_cmd *command_struct;
557
558         sc = command->sc;
559         if (error) {
560                 command->status.value = IPS_ERROR_STATUS;
561                 ips_insert_free_cmd(sc, command);
562                 printf("ips: error = %d in ips_read_nvram_callback\n", error);
563                 return;
564         }
565         command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
566         command_struct->command = IPS_RW_NVRAM_CMD;
567         command_struct->id = command->id;
568         command_struct->pagenum = 5;
569         command_struct->rw = 0;
570         command_struct->buffaddr = segments[0].ds_addr;
571
572         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
573             BUS_DMASYNC_PREWRITE);
574         bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
575             BUS_DMASYNC_PREREAD);
576         sc->ips_issue_cmd(command);
577 }
578
579 static int
580 ips_read_nvram(ips_command_t *command)
581 {
582         int error = 0;
583         ips_softc_t *sc = command->sc;
584
585         if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
586                                 /* alignemnt */ 1,
587                                 /* boundary  */ 0,
588                                 /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
589                                 /* highaddr  */ BUS_SPACE_MAXADDR,
590                                 /* filter    */ NULL,
591                                 /* filterarg */ NULL,
592                                 /* maxsize   */ IPS_NVRAM_PAGE_SIZE,
593                                 /* numsegs   */ 1,
594                                 /* maxsegsize*/ IPS_NVRAM_PAGE_SIZE,
595                                 /* flags     */ 0,
596                                 &command->data_dmatag) != 0) {
597                 printf("ips: can't alloc dma tag for nvram\n");
598                 error = ENOMEM;
599                 goto exit;
600         }
601         if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
602             BUS_DMA_NOWAIT, &command->data_dmamap)) {
603                 error = ENOMEM;
604                 goto exit;
605         }
606         command->callback = ips_write_nvram;
607         bus_dmamap_load(command->data_dmatag, command->data_dmamap,
608             command->data_buffer, IPS_NVRAM_PAGE_SIZE, ips_read_nvram_callback,
609             command, BUS_DMA_NOWAIT);
610         if ((command->status.value == IPS_ERROR_STATUS) ||
611             ips_timed_wait(command, "ips", 0) != 0)
612                 error = ETIMEDOUT;
613         if (error == 0) {
614                 bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
615                                 BUS_DMASYNC_POSTWRITE);
616         }
617         bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
618 exit:
619         bus_dmamem_free(command->data_dmatag, command->data_buffer,
620                         command->data_dmamap);
621         bus_dma_tag_destroy(command->data_dmatag);
622         ips_insert_free_cmd(sc, command);
623         return error;
624 }
625
626 int
627 ips_update_nvram(ips_softc_t *sc)
628 {
629         ips_command_t *command;
630
631         if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
632                 device_printf(sc->dev, "ERROR: unable to get a command! "
633                     "can't update nvram\n");
634                 return 1;
635         }
636         ips_read_nvram(command);
637         if (COMMAND_ERROR(&command->status)) {
638                 device_printf(sc->dev, "ERROR: nvram update command failed!\n");
639                 return 1;
640         }
641         return 0;
642 }
643
644 static int
645 ips_send_config_sync_cmd(ips_command_t *command)
646 {
647         ips_softc_t *sc = command->sc;
648         ips_generic_cmd *command_struct;
649
650         PRINTF(10, "ips test: got a command, building flush command\n");
651         command->callback = ips_wakeup_callback;
652         command_struct = (ips_generic_cmd *)command->command_buffer;
653         command_struct->command = IPS_CONFIG_SYNC_CMD;
654         command_struct->id = command->id;
655         command_struct->reserve2 = IPS_POCL;
656         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
657             BUS_DMASYNC_PREWRITE);
658         sc->ips_issue_cmd(command);
659         if (command->status.value != IPS_ERROR_STATUS)
660                 ips_timed_wait(command, "ipssyn", 0);
661         ips_insert_free_cmd(sc, command);
662         return 0;
663 }
664
665 static int
666 ips_send_error_table_cmd(ips_command_t *command)
667 {
668         ips_softc_t *sc = command->sc;
669         ips_generic_cmd *command_struct;
670
671         PRINTF(10, "ips test: got a command, building errortable command\n");
672         command->callback = ips_wakeup_callback;
673         command_struct = (ips_generic_cmd *)command->command_buffer;
674         command_struct->command = IPS_ERROR_TABLE_CMD;
675         command_struct->id = command->id;
676         command_struct->reserve2 = IPS_CSL;
677         bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
678             BUS_DMASYNC_PREWRITE);
679         sc->ips_issue_cmd(command);
680         if (command->status.value != IPS_ERROR_STATUS)
681                 ips_timed_wait(command, "ipsetc", 0);
682         ips_insert_free_cmd(sc, command);
683         return 0;
684 }
685
686 int
687 ips_clear_adapter(ips_softc_t *sc)
688 {
689         ips_command_t *command;
690
691         device_printf(sc->dev, "syncing config\n");
692         if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
693                 device_printf(sc->dev, "ERROR: unable to get a command! "
694                     "can't sync cache!\n");
695                 return 1;
696         }
697         ips_send_config_sync_cmd(command);
698         if (COMMAND_ERROR(&command->status)) {
699                 device_printf(sc->dev, "ERROR: cache sync command failed!\n");
700                 return 1;
701         }
702         device_printf(sc->dev, "clearing error table\n");
703         if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
704                 device_printf(sc->dev, "ERROR: unable to get a command! "
705                     "can't sync cache!\n");
706                 return 1;
707         }
708         ips_send_error_table_cmd(command);
709         if (COMMAND_ERROR(&command->status)) {
710                 device_printf(sc->dev, "ERROR: etable command failed!\n");
711                 return 1;
712         }
713         return 0;
714 }