xref: /dragonfly/sys/dev/raid/ips/ips_commands.c (revision 0bb9290e)
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.12 2006/03/24 18:35:32 dillon 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 		printf("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 	/*
160 	 * the cast to long long below is necessary because our b_pblkno
161 	 * is 32bit wide whereas it's 64bit on FreeBSD-CURRENT.
162 	 */
163 	PRINTF(10, "ips test: command id: %d segments: %d "
164 		"pblkno: %lld length: %d, ds_len: %d\n", command->id, segnum,
165 		bio->bio_offset / IPS_BLKSIZE,
166 		length, segments[0].ds_len);
167 
168 	sc->ips_issue_cmd(command);
169 	return;
170 }
171 
172 static int
173 ips_send_io_request(ips_command_t *command, struct bio *bio)
174 {
175 	struct buf *bp = bio->bio_buf;
176 
177 	command->callback = ips_io_request_finish;
178 	command->arg = bio;
179 	PRINTF(10, "ips test: : bcount %ld\n", bp->b_bcount);
180 	bus_dmamap_load(command->data_dmatag, command->data_dmamap,
181 			bp->b_data, bp->b_bcount,
182 			ips_io_request_callback, command, 0);
183 	return 0;
184 }
185 
186 void
187 ips_start_io_request(ips_softc_t *sc)
188 {
189 	ips_command_t *command;
190 	struct bio *bio;
191 
192 	bio = bioq_first(&sc->bio_queue);
193 	if (bio == NULL)
194 		return;
195 	if (ips_get_free_cmd(sc, &command, 0) != 0)
196 		return;
197 	bioq_remove(&sc->bio_queue, bio);
198 	ips_send_io_request(command, bio);
199 }
200 
201 /*
202  * Below are a series of functions for sending an adapter info request
203  * to the adapter.  The flow order is: get, send, callback. It uses
204  * the generic finish callback at the top of this file.
205  * This can be used to get configuration/status info from the card
206  */
207 static void
208 ips_adapter_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum,
209 			  int error)
210 {
211 	ips_softc_t *sc;
212 	ips_command_t *command = cmdptr;
213 	ips_adapter_info_cmd *command_struct;
214 	sc = command->sc;
215 	if (error) {
216 		command->status.value = IPS_ERROR_STATUS; /* a lovely error value */
217 		ips_insert_free_cmd(sc, command);
218 		printf("ips: error = %d in ips_get_adapter_info\n", error);
219 		return;
220 	}
221 	command_struct = (ips_adapter_info_cmd *)command->command_buffer;
222 	command_struct->command = IPS_ADAPTER_INFO_CMD;
223 	command_struct->id = command->id;
224 	command_struct->buffaddr = segments[0].ds_addr;
225 
226 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
227 			BUS_DMASYNC_PREWRITE);
228 	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
229 			BUS_DMASYNC_PREREAD);
230 	sc->ips_issue_cmd(command);
231 }
232 
233 static int
234 ips_send_adapter_info_cmd(ips_command_t *command)
235 {
236 	ips_softc_t *sc = command->sc;
237 	int error = 0;
238 
239 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
240 				/* alignemnt */	1,
241 				/* boundary  */	0,
242 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
243 				/* highaddr  */	BUS_SPACE_MAXADDR,
244 				/* filter    */	NULL,
245 				/* filterarg */	NULL,
246 				/* maxsize   */	IPS_ADAPTER_INFO_LEN,
247 				/* numsegs   */	1,
248 				/* maxsegsize*/	IPS_ADAPTER_INFO_LEN,
249 				/* flags     */	0,
250 				&command->data_dmatag) != 0) {
251 		printf("ips: can't alloc dma tag for adapter status\n");
252 		error = ENOMEM;
253 		goto exit;
254 	}
255 	if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
256 	   BUS_DMA_NOWAIT, &command->data_dmamap)) {
257 		error = ENOMEM;
258 		goto exit;
259 	}
260 	command->callback = ips_wakeup_callback;
261 	bus_dmamap_load(command->data_dmatag, command->data_dmamap,
262 	    command->data_buffer, IPS_ADAPTER_INFO_LEN,
263 	    ips_adapter_info_callback, command, BUS_DMA_NOWAIT);
264 	if ((command->status.value == IPS_ERROR_STATUS) ||
265 	    ips_timed_wait(command, "ips", 30 * hz) != 0)
266 		error = ETIMEDOUT;
267 	if (error == 0) {
268 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
269 		    BUS_DMASYNC_POSTREAD);
270 		memcpy(&(sc->adapter_info), command->data_buffer,
271 			IPS_ADAPTER_INFO_LEN);
272 	}
273 	bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
274 exit:
275 	/* I suppose I should clean up my memory allocations */
276 	bus_dmamem_free(command->data_dmatag, command->data_buffer,
277 	    command->data_dmamap);
278 	bus_dma_tag_destroy(command->data_dmatag);
279 	ips_insert_free_cmd(sc, command);
280 	return error;
281 }
282 
283 int
284 ips_get_adapter_info(ips_softc_t *sc)
285 {
286 	ips_command_t *command;
287 	int error = 0;
288 
289 	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
290 		device_printf(sc->dev, "unable to get adapter configuration\n");
291 		return ENXIO;
292 	}
293 	ips_send_adapter_info_cmd(command);
294 	if (COMMAND_ERROR(&command->status))
295 		error = ENXIO;
296 	return error;
297 }
298 
299 /*
300  * Below are a series of functions for sending a drive info request
301  * to the adapter.  The flow order is: get, send, callback. It uses
302  * the generic finish callback at the top of this file.
303  * This can be used to get drive status info from the card
304  */
305 static void
306 ips_drive_info_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
307 			int error)
308 {
309 	ips_softc_t *sc;
310 	ips_command_t *command = cmdptr;
311 	ips_drive_cmd *command_struct;
312 
313 	sc = command->sc;
314 	if (error) {
315 
316 		command->status.value = IPS_ERROR_STATUS;
317 		ips_insert_free_cmd(sc, command);
318 		printf("ips: error = %d in ips_get_drive_info\n", error);
319 		return;
320 	}
321 	command_struct = (ips_drive_cmd *)command->command_buffer;
322 	command_struct->command = IPS_DRIVE_INFO_CMD;
323 	command_struct->id = command->id;
324 	command_struct->buffaddr = segments[0].ds_addr;
325 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
326 	    BUS_DMASYNC_PREWRITE);
327 	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
328 	    BUS_DMASYNC_PREREAD);
329 	sc->ips_issue_cmd(command);
330 }
331 
332 static int
333 ips_send_drive_info_cmd(ips_command_t *command)
334 {
335 	int error = 0;
336 	ips_softc_t *sc = command->sc;
337 	ips_drive_info_t *driveinfo;
338 
339 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
340 				/* alignemnt */	1,
341 				/* boundary  */	0,
342 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
343 				/* highaddr  */	BUS_SPACE_MAXADDR,
344 				/* filter    */	NULL,
345 				/* filterarg */	NULL,
346 				/* maxsize   */	IPS_DRIVE_INFO_LEN,
347 				/* numsegs   */	1,
348 				/* maxsegsize*/	IPS_DRIVE_INFO_LEN,
349 				/* flags     */	0,
350 				&command->data_dmatag) != 0) {
351 		printf("ips: can't alloc dma tag for drive status\n");
352 		error = ENOMEM;
353 		goto exit;
354 	}
355 	if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
356 	    BUS_DMA_NOWAIT, &command->data_dmamap)) {
357 		error = ENOMEM;
358 		goto exit;
359 	}
360 	command->callback = ips_wakeup_callback;
361 	bus_dmamap_load(command->data_dmatag, command->data_dmamap,
362 	    command->data_buffer,IPS_DRIVE_INFO_LEN,
363 	    ips_drive_info_callback, command, BUS_DMA_NOWAIT);
364 	if ((command->status.value == IPS_ERROR_STATUS) ||
365 	    ips_timed_wait(command, "ips", 10 * hz) != 0)
366 		error = ETIMEDOUT;
367 
368 	if (error == 0) {
369 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
370 		    BUS_DMASYNC_POSTREAD);
371 		driveinfo = command->data_buffer;
372 		memcpy(sc->drives, driveinfo->drives, sizeof(ips_drive_t) * 8);
373 		sc->drivecount = driveinfo->drivecount;
374 		device_printf(sc->dev, "logical drives: %d\n", sc->drivecount);
375 	}
376 	bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
377 exit:
378 	/* I suppose I should clean up my memory allocations */
379 	bus_dmamem_free(command->data_dmatag, command->data_buffer,
380 			command->data_dmamap);
381 	bus_dma_tag_destroy(command->data_dmatag);
382 	ips_insert_free_cmd(sc, command);
383 	return error;
384 }
385 
386 int
387 ips_get_drive_info(ips_softc_t *sc)
388 {
389 	int error = 0;
390 	ips_command_t *command;
391 
392 	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
393 		device_printf(sc->dev, "unable to get drive configuration\n");
394 		return ENXIO;
395 	}
396 	ips_send_drive_info_cmd(command);
397 	if (COMMAND_ERROR(&command->status))
398 		error = ENXIO;
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_generic_cmd *command_struct;
411 
412 	PRINTF(10,"ips test: got a command, building flush command\n");
413 	command->callback = ips_wakeup_callback;
414 	command_struct = (ips_generic_cmd *)command->command_buffer;
415 	command_struct->command = IPS_CACHE_FLUSH_CMD;
416 	command_struct->id = command->id;
417 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
418 	    BUS_DMASYNC_PREWRITE);
419 	sc->ips_issue_cmd(command);
420 	if (command->status.value != IPS_ERROR_STATUS)
421 		ips_timed_wait(command, "flush2", 0);
422 	ips_insert_free_cmd(sc, command);
423 	return 0;
424 }
425 
426 int
427 ips_flush_cache(ips_softc_t *sc)
428 {
429 	ips_command_t *command;
430 
431 	device_printf(sc->dev, "flushing cache\n");
432 	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
433 		device_printf(sc->dev, "ERROR: unable to get a command! "
434 		    "can't flush cache!\n");
435 		return(1);
436 	}
437 	ips_send_flush_cache_cmd(command);
438 	if (COMMAND_ERROR(&command->status)) {
439 		device_printf(sc->dev, "ERROR: cache flush command failed!\n");
440 		return(1);
441 	}
442 	return 0;
443 }
444 
445 /*
446  * Simplified localtime to provide timevalues for ffdc.
447  * Taken from libc/stdtime/localtime.c
448  */
449 static void
450 ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime)
451 {
452 	long	days, rem, y;
453 	int	yleap, *ip, month;
454 	int	year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR };
455 	int	mon_lengths[2][IPS_MONSPERYEAR] = {
456 		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
457 		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
458 	};
459 
460 	days = sctime / IPS_SECSPERDAY;
461 	rem  = sctime % IPS_SECSPERDAY;
462 
463 	command->hour = rem / IPS_SECSPERHOUR;
464 	rem	      = rem % IPS_SECSPERHOUR;
465 
466 	command->minute = rem / IPS_SECSPERMIN;
467 	command->second = rem % IPS_SECSPERMIN;
468 
469 	y = IPS_EPOCH_YEAR;
470 	while (days < 0 || days >= (long)year_lengths[yleap = ips_isleap(y)]) {
471 		long    newy;
472 
473 		newy = y + days / IPS_DAYSPERNYEAR;
474 		if (days < 0)
475 			--newy;
476 		days -= (newy - y) * IPS_DAYSPERNYEAR +
477 		    IPS_LEAPS_THRU_END_OF(newy - 1) -
478 		    IPS_LEAPS_THRU_END_OF(y - 1);
479 		y = newy;
480 	}
481 	command->yearH = y / 100;
482 	command->yearL = y % 100;
483 	ip = mon_lengths[yleap];
484 	for (month = 0; days >= (long)ip[month]; ++month)
485 		days = days - (long)ip[month];
486 	command->month = month + 1;
487 	command->day = days + 1;
488 }
489 
490 static int
491 ips_send_ffdc_reset_cmd(ips_command_t *command)
492 {
493 	ips_softc_t *sc = command->sc;
494 	ips_adapter_ffdc_cmd *command_struct;
495 
496 	PRINTF(10, "ips test: got a command, building ffdc reset command\n");
497 	command->callback = ips_wakeup_callback;
498 	command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer;
499 	command_struct->command = IPS_FFDC_CMD;
500 	command_struct->id = command->id;
501 	command_struct->reset_count = sc->ffdc_resetcount;
502 	command_struct->reset_type  = 0x0;
503 	ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec);
504 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
505 	    BUS_DMASYNC_PREWRITE);
506 	sc->ips_issue_cmd(command);
507 	if (command->status.value != IPS_ERROR_STATUS)
508 		ips_timed_wait(command, "ffdc", 0);
509 	ips_insert_free_cmd(sc, command);
510 	return 0;
511 }
512 
513 int
514 ips_ffdc_reset(ips_softc_t *sc)
515 {
516 	ips_command_t *command;
517 
518 	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
519 		device_printf(sc->dev, "ERROR: unable to get a command! "
520 		    "can't send ffdc reset!\n");
521 		return 1;
522 	}
523 	ips_send_ffdc_reset_cmd(command);
524 	if (COMMAND_ERROR(&command->status)) {
525 		/*
526 		 * apparently some cards may report error status for
527 		 * an ffdc reset command, even though it works correctly
528 		 * afterwards.  just complain about that and proceed here.
529 		 */
530 		device_printf(sc->dev,
531 			      "ERROR: ffdc reset command failed(0x%04x)!\n",
532 			      command->status.value);
533 	}
534 	return 0;
535 }
536 
537 static void
538 ips_write_nvram(ips_command_t *command)
539 {
540 	ips_softc_t *sc = command->sc;
541 	ips_rw_nvram_cmd *command_struct;
542 	ips_nvram_page5 *nvram;
543 
544 	/*FIXME check for error */
545 	command->callback = ips_wakeup_callback;
546 	command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
547 	command_struct->command = IPS_RW_NVRAM_CMD;
548 	command_struct->id = command->id;
549 	command_struct->pagenum = 5;
550 	command_struct->rw	= 1;	/* write */
551 	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
552 	    BUS_DMASYNC_POSTREAD);
553 	nvram = command->data_buffer;
554 	/* retrieve adapter info and save in sc */
555 	sc->adapter_type = nvram->adapter_type;
556 	strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4);
557 	strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4);
558 	nvram->operating_system = IPS_OS_FREEBSD;
559 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
560 	    BUS_DMASYNC_PREWRITE);
561 	sc->ips_issue_cmd(command);
562 }
563 
564 static void
565 ips_read_nvram_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
566 			int error)
567 {
568 	ips_softc_t *sc;
569 	ips_command_t *command = cmdptr;
570 	ips_rw_nvram_cmd *command_struct;
571 
572 	sc = command->sc;
573 	if (error) {
574 		command->status.value = IPS_ERROR_STATUS;
575 		ips_insert_free_cmd(sc, command);
576 		printf("ips: error = %d in ips_read_nvram_callback\n", error);
577 		return;
578 	}
579 	command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
580 	command_struct->command = IPS_RW_NVRAM_CMD;
581 	command_struct->id = command->id;
582 	command_struct->pagenum = 5;
583 	command_struct->rw = 0;
584 	command_struct->buffaddr = segments[0].ds_addr;
585 
586 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
587 	    BUS_DMASYNC_PREWRITE);
588 	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
589 	    BUS_DMASYNC_PREREAD);
590 	sc->ips_issue_cmd(command);
591 }
592 
593 static int
594 ips_read_nvram(ips_command_t *command)
595 {
596 	int error = 0;
597 	ips_softc_t *sc = command->sc;
598 
599 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
600 				/* alignemnt */	1,
601 				/* boundary  */	0,
602 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
603 				/* highaddr  */	BUS_SPACE_MAXADDR,
604 				/* filter    */	NULL,
605 				/* filterarg */	NULL,
606 				/* maxsize   */	IPS_NVRAM_PAGE_SIZE,
607 				/* numsegs   */	1,
608 				/* maxsegsize*/	IPS_NVRAM_PAGE_SIZE,
609 				/* flags     */	0,
610 				&command->data_dmatag) != 0) {
611 		printf("ips: can't alloc dma tag for nvram\n");
612 		error = ENOMEM;
613 		goto exit;
614 	}
615 	if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
616 	    BUS_DMA_NOWAIT, &command->data_dmamap)) {
617 		error = ENOMEM;
618 		goto exit;
619 	}
620 	command->callback = ips_write_nvram;
621 	bus_dmamap_load(command->data_dmatag, command->data_dmamap,
622 	    command->data_buffer, IPS_NVRAM_PAGE_SIZE, ips_read_nvram_callback,
623 	    command, BUS_DMA_NOWAIT);
624 	if ((command->status.value == IPS_ERROR_STATUS) ||
625 	    ips_timed_wait(command, "ips", 0) != 0)
626 		error = ETIMEDOUT;
627 	if (error == 0) {
628 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
629 				BUS_DMASYNC_POSTWRITE);
630 	}
631 	bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
632 exit:
633 	bus_dmamem_free(command->data_dmatag, command->data_buffer,
634 			command->data_dmamap);
635 	bus_dma_tag_destroy(command->data_dmatag);
636 	ips_insert_free_cmd(sc, command);
637 	return error;
638 }
639 
640 int
641 ips_update_nvram(ips_softc_t *sc)
642 {
643 	ips_command_t *command;
644 
645 	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
646 		device_printf(sc->dev, "ERROR: unable to get a command! "
647 		    "can't update nvram\n");
648 		return 1;
649 	}
650 	ips_read_nvram(command);
651 	if (COMMAND_ERROR(&command->status)) {
652 		device_printf(sc->dev, "ERROR: nvram update command failed!\n");
653 		return 1;
654 	}
655 	return 0;
656 }
657 
658 static int
659 ips_send_config_sync_cmd(ips_command_t *command)
660 {
661 	ips_softc_t *sc = command->sc;
662 	ips_generic_cmd *command_struct;
663 
664 	PRINTF(10, "ips test: got a command, building flush command\n");
665 	command->callback = ips_wakeup_callback;
666 	command_struct = (ips_generic_cmd *)command->command_buffer;
667 	command_struct->command = IPS_CONFIG_SYNC_CMD;
668 	command_struct->id = command->id;
669 	command_struct->reserve2 = IPS_POCL;
670 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
671 	    BUS_DMASYNC_PREWRITE);
672 	sc->ips_issue_cmd(command);
673 	if (command->status.value != IPS_ERROR_STATUS)
674 		ips_timed_wait(command, "ipssyn", 0);
675 	ips_insert_free_cmd(sc, command);
676 	return 0;
677 }
678 
679 static int
680 ips_send_error_table_cmd(ips_command_t *command)
681 {
682 	ips_softc_t *sc = command->sc;
683 	ips_generic_cmd *command_struct;
684 
685 	PRINTF(10, "ips test: got a command, building errortable command\n");
686 	command->callback = ips_wakeup_callback;
687 	command_struct = (ips_generic_cmd *)command->command_buffer;
688 	command_struct->command = IPS_ERROR_TABLE_CMD;
689 	command_struct->id = command->id;
690 	command_struct->reserve2 = IPS_CSL;
691 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
692 	    BUS_DMASYNC_PREWRITE);
693 	sc->ips_issue_cmd(command);
694 	if (command->status.value != IPS_ERROR_STATUS)
695 		ips_timed_wait(command, "ipsetc", 0);
696 	ips_insert_free_cmd(sc, command);
697 	return 0;
698 }
699 
700 int
701 ips_clear_adapter(ips_softc_t *sc)
702 {
703 	ips_command_t *command;
704 
705 	device_printf(sc->dev, "syncing config\n");
706 	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
707 		device_printf(sc->dev, "ERROR: unable to get a command! "
708 		    "can't sync cache!\n");
709 		return 1;
710 	}
711 	ips_send_config_sync_cmd(command);
712 	if (COMMAND_ERROR(&command->status)) {
713 		device_printf(sc->dev, "ERROR: cache sync command failed!\n");
714 		return 1;
715 	}
716 	device_printf(sc->dev, "clearing error table\n");
717 	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) != 0) {
718 		device_printf(sc->dev, "ERROR: unable to get a command! "
719 		    "can't sync cache!\n");
720 		return 1;
721 	}
722 	ips_send_error_table_cmd(command);
723 	if (COMMAND_ERROR(&command->status)) {
724 		device_printf(sc->dev, "ERROR: etable command failed!\n");
725 		return 1;
726 	}
727 	return 0;
728 }
729