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