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