xref: /dragonfly/sys/dev/raid/ips/ips_commands.c (revision 0e085424)
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 }
715