1114902Sscottl/*-
2119418Sobrien * Written by: David Jeffery
3114902Sscottl * Copyright (c) 2002 Adaptec Inc.
4114902Sscottl * All rights reserved.
5114902Sscottl *
6114902Sscottl * Redistribution and use in source and binary forms, with or without
7114902Sscottl * modification, are permitted provided that the following conditions
8114902Sscottl * are met:
9114902Sscottl * 1. Redistributions of source code must retain the above copyright
10114902Sscottl *    notice, this list of conditions and the following disclaimer.
11114902Sscottl * 2. Redistributions in binary form must reproduce the above copyright
12114902Sscottl *    notice, this list of conditions and the following disclaimer in the
13114902Sscottl *    documentation and/or other materials provided with the distribution.
14114902Sscottl *
15114902Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16114902Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17114902Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18114902Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19114902Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20114902Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21114902Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22114902Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23114902Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24114902Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25114902Sscottl * SUCH DAMAGE.
26114902Sscottl */
27114902Sscottl
28119418Sobrien#include <sys/cdefs.h>
29119418Sobrien__FBSDID("$FreeBSD$");
30114902Sscottl
31152919Sscottl#include <dev/ips/ipsreg.h>
32114902Sscottl#include <dev/ips/ips.h>
33114902Sscottl
34114902Sscottl/*
35114902Sscottl * This is an interrupt callback.  It is called from
36114902Sscottl * interrupt context when the adapter has completed the
37114902Sscottl * command.  This very generic callback simply stores
38114902Sscottl * the command's return value in command->arg and wake's
39114902Sscottl * up anyone waiting on the command.
40114902Sscottl */
41114902Sscottlstatic void ips_wakeup_callback(ips_command_t *command)
42114902Sscottl{
43114902Sscottl	bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
44114902Sscottl			BUS_DMASYNC_POSTWRITE);
45140923Sscottl	sema_post(&command->sc->cmd_sema);
46114902Sscottl}
47114902Sscottl/* Below are a series of functions for sending an IO request
48114902Sscottl * to the adapter.  The flow order is: start, send, callback, finish.
49114902Sscottl * The caller must have already assembled an iorequest struct to hold
50114902Sscottl * the details of the IO request. */
51114902Sscottlstatic void ips_io_request_finish(ips_command_t *command)
52114902Sscottl{
53114902Sscottl
54114902Sscottl	struct bio *iobuf = command->arg;
55114902Sscottl	if(ips_read_request(iobuf)) {
56114902Sscottl		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
57114902Sscottl				BUS_DMASYNC_POSTREAD);
58114902Sscottl	} else {
59114902Sscottl		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
60114902Sscottl				BUS_DMASYNC_POSTWRITE);
61114902Sscottl	}
62114902Sscottl	bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
63150535Sscottl	if(COMMAND_ERROR(command)){
64114902Sscottl		iobuf->bio_flags |=BIO_ERROR;
65114902Sscottl		iobuf->bio_error = EIO;
66150611Sglebius		printf("ips: io error, status= 0x%x\n", command->status.value);
67114902Sscottl	}
68114902Sscottl	ips_insert_free_cmd(command->sc, command);
69114902Sscottl	ipsd_finish(iobuf);
70114902Sscottl}
71114902Sscottl
72114902Sscottlstatic void ips_io_request_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
73114902Sscottl{
74114902Sscottl	ips_softc_t *sc;
75114902Sscottl	ips_command_t *command = cmdptr;
76114902Sscottl	ips_sg_element_t *sg_list;
77114902Sscottl	ips_io_cmd *command_struct;
78114902Sscottl	struct bio *iobuf = command->arg;
79114902Sscottl	int i, length = 0;
80114902Sscottl	u_int8_t cmdtype;
81114902Sscottl
82114902Sscottl	sc = command->sc;
83114902Sscottl	if(error){
84114902Sscottl		printf("ips: error = %d in ips_sg_request_callback\n", error);
85114902Sscottl		bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
86114902Sscottl		iobuf->bio_flags |= BIO_ERROR;
87114902Sscottl		iobuf->bio_error = ENOMEM;
88114902Sscottl		ips_insert_free_cmd(sc, command);
89114902Sscottl		ipsd_finish(iobuf);
90114902Sscottl		return;
91114902Sscottl	}
92114902Sscottl	command_struct = (ips_io_cmd *)command->command_buffer;
93114902Sscottl	command_struct->id = command->id;
94116931Speter	command_struct->drivenum = (uintptr_t)iobuf->bio_driver1;
95114902Sscottl	if(segnum != 1){
96114902Sscottl		if(ips_read_request(iobuf))
97114902Sscottl			cmdtype = IPS_SG_READ_CMD;
98114902Sscottl		else
99114902Sscottl			cmdtype = IPS_SG_WRITE_CMD;
100114902Sscottl		command_struct->segnum = segnum;
101114902Sscottl		sg_list = (ips_sg_element_t *)((u_int8_t *)
102114902Sscottl			   command->command_buffer + IPS_COMMAND_LEN);
103114902Sscottl		for(i = 0; i < segnum; i++){
104114902Sscottl			sg_list[i].addr = segments[i].ds_addr;
105114902Sscottl			sg_list[i].len = segments[i].ds_len;
106114902Sscottl			length += segments[i].ds_len;
107114902Sscottl		}
108114902Sscottl		command_struct->buffaddr =
109114902Sscottl	    	    (u_int32_t)command->command_phys_addr + IPS_COMMAND_LEN;
110114902Sscottl	} else {
111114902Sscottl		if(ips_read_request(iobuf))
112114902Sscottl			cmdtype = IPS_READ_CMD;
113114902Sscottl		else
114114902Sscottl			cmdtype = IPS_WRITE_CMD;
115114902Sscottl		command_struct->buffaddr = segments[0].ds_addr;
116114902Sscottl		length = segments[0].ds_len;
117114902Sscottl	}
118114902Sscottl	command_struct->command = cmdtype;
119114902Sscottl	command_struct->lba = iobuf->bio_pblkno;
120114902Sscottl	length = (length + IPS_BLKSIZE - 1)/IPS_BLKSIZE;
121114902Sscottl	command_struct->length = length;
122114902Sscottl	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
123114902Sscottl			BUS_DMASYNC_PREWRITE);
124114902Sscottl	if(ips_read_request(iobuf)) {
125114902Sscottl		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
126114902Sscottl				BUS_DMASYNC_PREREAD);
127114902Sscottl	} else {
128114902Sscottl		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
129114902Sscottl				BUS_DMASYNC_PREWRITE);
130114902Sscottl	}
131121211Sphk	PRINTF(10, "ips test: command id: %d segments: %d "
132114902Sscottl		"pblkno: %lld length: %d, ds_len: %d\n", command->id, segnum,
133121211Sphk		iobuf->bio_pblkno,
134114902Sscottl		length, segments[0].ds_len);
135114902Sscottl
136114902Sscottl	sc->ips_issue_cmd(command);
137114902Sscottl	return;
138114902Sscottl}
139114902Sscottl
140140923Sscottlstatic int ips_send_io_request(ips_command_t *command, struct bio *iobuf)
141114902Sscottl{
142114902Sscottl	command->callback = ips_io_request_finish;
143140923Sscottl	command->arg = iobuf;
144114902Sscottl	PRINTF(10, "ips test: : bcount %ld\n", iobuf->bio_bcount);
145114902Sscottl	bus_dmamap_load(command->data_dmatag, command->data_dmamap,
146114902Sscottl			iobuf->bio_data, iobuf->bio_bcount,
147114902Sscottl			ips_io_request_callback, command, 0);
148114902Sscottl	return 0;
149114902Sscottl}
150114902Sscottl
151126364Sscottlvoid ips_start_io_request(ips_softc_t *sc)
152114902Sscottl{
153126364Sscottl	struct bio *iobuf;
154140923Sscottl	ips_command_t *command;
155126364Sscottl
156126364Sscottl	iobuf = bioq_first(&sc->queue);
157140923Sscottl	if(!iobuf)
158114902Sscottl		return;
159126364Sscottl
160140923Sscottl	if (ips_get_free_cmd(sc, &command, 0))
161126364Sscottl		return;
162126364Sscottl
163126364Sscottl	bioq_remove(&sc->queue, iobuf);
164140923Sscottl	ips_send_io_request(command, iobuf);
165114902Sscottl	return;
166114902Sscottl}
167114902Sscottl
168114902Sscottl/* Below are a series of functions for sending an adapter info request
169114902Sscottl * to the adapter.  The flow order is: get, send, callback. It uses
170114902Sscottl * the generic finish callback at the top of this file.
171114902Sscottl * This can be used to get configuration/status info from the card */
172114902Sscottlstatic void ips_adapter_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
173114902Sscottl{
174114902Sscottl	ips_softc_t *sc;
175114902Sscottl	ips_command_t *command = cmdptr;
176114902Sscottl	ips_adapter_info_cmd *command_struct;
177114902Sscottl	sc = command->sc;
178114902Sscottl	if(error){
179150535Sscottl		ips_set_error(command, error);
180114902Sscottl		printf("ips: error = %d in ips_get_adapter_info\n", error);
181114902Sscottl		return;
182114902Sscottl	}
183114902Sscottl	command_struct = (ips_adapter_info_cmd *)command->command_buffer;
184114902Sscottl	command_struct->command = IPS_ADAPTER_INFO_CMD;
185114902Sscottl	command_struct->id = command->id;
186114902Sscottl	command_struct->buffaddr = segments[0].ds_addr;
187114902Sscottl
188114902Sscottl	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
189114902Sscottl			BUS_DMASYNC_PREWRITE);
190114902Sscottl	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
191114902Sscottl			BUS_DMASYNC_PREREAD);
192114902Sscottl	sc->ips_issue_cmd(command);
193150535Sscottl	if (sema_timedwait(&sc->cmd_sema, 30*hz) != 0) {
194150535Sscottl		ips_set_error(command, ETIMEDOUT);
195150535Sscottl		return;
196150535Sscottl	}
197150535Sscottl
198150535Sscottl	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
199150535Sscottl			BUS_DMASYNC_POSTREAD);
200150535Sscottl	memcpy(&(sc->adapter_info), command->data_buffer, IPS_ADAPTER_INFO_LEN);
201114902Sscottl}
202114902Sscottl
203114902Sscottl
204114902Sscottl
205114902Sscottlstatic int ips_send_adapter_info_cmd(ips_command_t *command)
206114902Sscottl{
207114902Sscottl	int error = 0;
208114902Sscottl	ips_softc_t *sc = command->sc;
209114902Sscottl
210114902Sscottl	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
211114902Sscottl				/* alignemnt */	1,
212114902Sscottl				/* boundary  */	0,
213114902Sscottl				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
214114902Sscottl				/* highaddr  */	BUS_SPACE_MAXADDR,
215114902Sscottl				/* filter    */	NULL,
216114902Sscottl				/* filterarg */	NULL,
217114902Sscottl				/* maxsize   */	IPS_ADAPTER_INFO_LEN,
218114902Sscottl				/* numsegs   */	1,
219114902Sscottl				/* maxsegsize*/	IPS_ADAPTER_INFO_LEN,
220114902Sscottl				/* flags     */	0,
221140923Sscottl				/* lockfunc  */ NULL,
222140923Sscottl				/* lockarg   */ NULL,
223114902Sscottl				&command->data_dmatag) != 0) {
224114902Sscottl                printf("ips: can't alloc dma tag for adapter status\n");
225114902Sscottl		error = ENOMEM;
226114902Sscottl		goto exit;
227114902Sscottl        }
228114902Sscottl	if(bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
229114902Sscottl	   BUS_DMA_NOWAIT, &command->data_dmamap)){
230114902Sscottl		error = ENOMEM;
231114902Sscottl		goto exit;
232114902Sscottl	}
233114902Sscottl	command->callback = ips_wakeup_callback;
234150535Sscottl	error = bus_dmamap_load(command->data_dmatag, command->data_dmamap,
235150535Sscottl				command->data_buffer,IPS_ADAPTER_INFO_LEN,
236150535Sscottl				ips_adapter_info_callback, command,
237150535Sscottl				BUS_DMA_NOWAIT);
238114902Sscottl
239150535Sscottl	if (error == 0)
240150535Sscottl		bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
241114902Sscottl
242114902Sscottlexit:
243114902Sscottl	/* I suppose I should clean up my memory allocations */
244114902Sscottl	bus_dmamem_free(command->data_dmatag, command->data_buffer,
245114902Sscottl			command->data_dmamap);
246114902Sscottl	bus_dma_tag_destroy(command->data_dmatag);
247114902Sscottl	ips_insert_free_cmd(sc, command);
248114902Sscottl	return error;
249114902Sscottl}
250114902Sscottl
251114902Sscottlint ips_get_adapter_info(ips_softc_t *sc)
252114902Sscottl{
253140923Sscottl	ips_command_t *command;
254114902Sscottl	int error = 0;
255140923Sscottl
256140923Sscottl	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) > 0){
257114902Sscottl		device_printf(sc->dev, "unable to get adapter configuration\n");
258114902Sscottl		return ENXIO;
259114902Sscottl	}
260140923Sscottl	ips_send_adapter_info_cmd(command);
261150535Sscottl	if (COMMAND_ERROR(command)){
262114902Sscottl		error = ENXIO;
263114902Sscottl	}
264114902Sscottl	return error;
265114902Sscottl}
266114902Sscottl
267114902Sscottl/* Below are a series of functions for sending a drive info request
268114902Sscottl * to the adapter.  The flow order is: get, send, callback. It uses
269114902Sscottl * the generic finish callback at the top of this file.
270114902Sscottl * This can be used to get drive status info from the card */
271114902Sscottlstatic void ips_drive_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
272114902Sscottl{
273114902Sscottl	ips_softc_t *sc;
274114902Sscottl	ips_command_t *command = cmdptr;
275114902Sscottl	ips_drive_cmd *command_struct;
276150535Sscottl	ips_drive_info_t *driveinfo;
277150535Sscottl
278114902Sscottl	sc = command->sc;
279114902Sscottl	if(error){
280150535Sscottl		ips_set_error(command, error);
281114902Sscottl		printf("ips: error = %d in ips_get_drive_info\n", error);
282114902Sscottl		return;
283114902Sscottl	}
284114902Sscottl	command_struct = (ips_drive_cmd *)command->command_buffer;
285114902Sscottl	command_struct->command = IPS_DRIVE_INFO_CMD;
286114902Sscottl	command_struct->id = command->id;
287114902Sscottl	command_struct->buffaddr = segments[0].ds_addr;
288114902Sscottl
289114902Sscottl	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
290114902Sscottl			BUS_DMASYNC_PREWRITE);
291114902Sscottl	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
292114902Sscottl			BUS_DMASYNC_PREREAD);
293114902Sscottl	sc->ips_issue_cmd(command);
294150535Sscottl	if (sema_timedwait(&sc->cmd_sema, 10*hz) != 0) {
295150535Sscottl		ips_set_error(command, ETIMEDOUT);
296150535Sscottl		return;
297150535Sscottl	}
298150535Sscottl
299150535Sscottl	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
300150535Sscottl			BUS_DMASYNC_POSTREAD);
301150535Sscottl	driveinfo = command->data_buffer;
302150535Sscottl	memcpy(sc->drives, driveinfo->drives, sizeof(ips_drive_t) * 8);
303150535Sscottl	sc->drivecount = driveinfo->drivecount;
304150535Sscottl	device_printf(sc->dev, "logical drives: %d\n",sc->drivecount);
305114902Sscottl}
306114902Sscottl
307114902Sscottlstatic int ips_send_drive_info_cmd(ips_command_t *command)
308114902Sscottl{
309114902Sscottl	int error = 0;
310114902Sscottl	ips_softc_t *sc = command->sc;
311114902Sscottl
312114902Sscottl	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
313114902Sscottl				/* alignemnt */	1,
314114902Sscottl				/* boundary  */	0,
315114902Sscottl				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
316114902Sscottl				/* highaddr  */	BUS_SPACE_MAXADDR,
317114902Sscottl				/* filter    */	NULL,
318114902Sscottl				/* filterarg */	NULL,
319114902Sscottl				/* maxsize   */	IPS_DRIVE_INFO_LEN,
320114902Sscottl				/* numsegs   */	1,
321114902Sscottl				/* maxsegsize*/	IPS_DRIVE_INFO_LEN,
322114902Sscottl				/* flags     */	0,
323140923Sscottl				/* lockfunc  */ NULL,
324140923Sscottl				/* lockarg   */ NULL,
325114902Sscottl				&command->data_dmatag) != 0) {
326114902Sscottl                printf("ips: can't alloc dma tag for drive status\n");
327114902Sscottl		error = ENOMEM;
328114902Sscottl		goto exit;
329114902Sscottl        }
330114902Sscottl	if(bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
331114902Sscottl	   		    BUS_DMA_NOWAIT, &command->data_dmamap)){
332114902Sscottl		error = ENOMEM;
333114902Sscottl		goto exit;
334114902Sscottl	}
335114902Sscottl	command->callback = ips_wakeup_callback;
336150535Sscottl	error = bus_dmamap_load(command->data_dmatag, command->data_dmamap,
337150535Sscottl				command->data_buffer,IPS_DRIVE_INFO_LEN,
338150535Sscottl				ips_drive_info_callback, command,
339150535Sscottl				BUS_DMA_NOWAIT);
340150535Sscottl	if (error == 0)
341150535Sscottl		bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
342114902Sscottl
343114902Sscottlexit:
344114902Sscottl	/* I suppose I should clean up my memory allocations */
345114902Sscottl	bus_dmamem_free(command->data_dmatag, command->data_buffer,
346114902Sscottl			command->data_dmamap);
347114902Sscottl	bus_dma_tag_destroy(command->data_dmatag);
348114902Sscottl	ips_insert_free_cmd(sc, command);
349114902Sscottl	return error;
350114902Sscottl
351114902Sscottl}
352114902Sscottlint ips_get_drive_info(ips_softc_t *sc)
353114902Sscottl{
354114902Sscottl	int error = 0;
355140923Sscottl	ips_command_t *command;
356140923Sscottl
357140923Sscottl	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) > 0){
358114902Sscottl		device_printf(sc->dev, "unable to get drive configuration\n");
359114902Sscottl		return ENXIO;
360114902Sscottl	}
361140923Sscottl	ips_send_drive_info_cmd(command);
362150535Sscottl	if(COMMAND_ERROR(command)){
363114902Sscottl		error = ENXIO;
364114902Sscottl	}
365114902Sscottl	return error;
366114902Sscottl}
367114902Sscottl
368114902Sscottl/* Below is a pair of functions for making sure data is safely
369114902Sscottl * on disk by flushing the adapter's cache. */
370114902Sscottlstatic int ips_send_flush_cache_cmd(ips_command_t *command)
371114902Sscottl{
372114902Sscottl	ips_softc_t *sc = command->sc;
373114902Sscottl	ips_generic_cmd *command_struct;
374114902Sscottl
375114902Sscottl	PRINTF(10,"ips test: got a command, building flush command\n");
376114902Sscottl	command->callback = ips_wakeup_callback;
377114902Sscottl	command_struct = (ips_generic_cmd *)command->command_buffer;
378114902Sscottl	command_struct->command = IPS_CACHE_FLUSH_CMD;
379114902Sscottl	command_struct->id = command->id;
380114902Sscottl	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
381114902Sscottl			BUS_DMASYNC_PREWRITE);
382114902Sscottl	sc->ips_issue_cmd(command);
383150535Sscottl	if (COMMAND_ERROR(command) == 0)
384140923Sscottl		sema_wait(&sc->cmd_sema);
385114902Sscottl	ips_insert_free_cmd(sc, command);
386114902Sscottl	return 0;
387114902Sscottl}
388114902Sscottl
389114902Sscottlint ips_flush_cache(ips_softc_t *sc)
390114902Sscottl{
391140923Sscottl	ips_command_t *command;
392140923Sscottl
393114902Sscottl	device_printf(sc->dev, "flushing cache\n");
394140923Sscottl	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){
395114902Sscottl		device_printf(sc->dev, "ERROR: unable to get a command! can't flush cache!\n");
396114902Sscottl	}
397140923Sscottl	ips_send_flush_cache_cmd(command);
398150535Sscottl	if(COMMAND_ERROR(command)){
399114902Sscottl		device_printf(sc->dev, "ERROR: cache flush command failed!\n");
400114902Sscottl	}
401114902Sscottl	return 0;
402114902Sscottl}
403114902Sscottl
404122999Smbr/* Simplified localtime to provide timevalues for ffdc.
405122999Smbr * Taken from libc/stdtime/localtime.c
406122999Smbr */
407122999Smbrvoid static ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime)
408122999Smbr{
409122999Smbr	long	days, rem, y;
410122999Smbr	int	yleap, *ip, month;
411122999Smbr	int	year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR };
412122999Smbr	int	mon_lengths[2][IPS_MONSPERYEAR] = {
413122999Smbr		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
414122999Smbr		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
415122999Smbr	};
416122999Smbr
417122999Smbr	days = sctime / IPS_SECSPERDAY;
418122999Smbr	rem  = sctime % IPS_SECSPERDAY;
419122999Smbr
420122999Smbr	command->hour = rem / IPS_SECSPERHOUR;
421122999Smbr	rem           = rem % IPS_SECSPERHOUR;
422122999Smbr
423122999Smbr	command->minute = rem / IPS_SECSPERMIN;
424122999Smbr	command->second = rem % IPS_SECSPERMIN;
425122999Smbr
426122999Smbr	y = IPS_EPOCH_YEAR;
427122999Smbr	while (days < 0 || days >= (long) year_lengths[yleap = ips_isleap(y)]) {
428122999Smbr		long    newy;
429122999Smbr
430122999Smbr		newy = y + days / IPS_DAYSPERNYEAR;
431122999Smbr		if (days < 0)
432122999Smbr			--newy;
433122999Smbr		days -= (newy - y) * IPS_DAYSPERNYEAR +
434122999Smbr			IPS_LEAPS_THRU_END_OF(newy - 1) -
435122999Smbr			IPS_LEAPS_THRU_END_OF(y - 1);
436122999Smbr		y = newy;
437122999Smbr	}
438122999Smbr	command->yearH = y / 100;
439122999Smbr	command->yearL = y % 100;
440122999Smbr	ip = mon_lengths[yleap];
441122999Smbr	for(month = 0; days >= (long) ip[month]; ++month)
442122999Smbr		days = days - (long) ip[month];
443122999Smbr	command->month = month + 1;
444122999Smbr	command->day = days + 1;
445122999Smbr}
446122999Smbr
447122999Smbrstatic int ips_send_ffdc_reset_cmd(ips_command_t *command)
448122999Smbr{
449122999Smbr	ips_softc_t *sc = command->sc;
450122999Smbr	ips_adapter_ffdc_cmd *command_struct;
451122999Smbr
452122999Smbr	PRINTF(10,"ips test: got a command, building ffdc reset command\n");
453122999Smbr	command->callback = ips_wakeup_callback;
454122999Smbr	command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer;
455122999Smbr	command_struct->command = IPS_FFDC_CMD;
456122999Smbr	command_struct->id = command->id;
457122999Smbr	command_struct->reset_count = sc->ffdc_resetcount;
458124040Smbr	command_struct->reset_type  = 0x0;
459122999Smbr	ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec);
460122999Smbr
461122999Smbr	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
462122999Smbr			BUS_DMASYNC_PREWRITE);
463122999Smbr	sc->ips_issue_cmd(command);
464150535Sscottl	if (COMMAND_ERROR(command) == 0)
465140923Sscottl		sema_wait(&sc->cmd_sema);
466122999Smbr	ips_insert_free_cmd(sc, command);
467122999Smbr	return 0;
468122999Smbr}
469122999Smbr
470122999Smbrint ips_ffdc_reset(ips_softc_t *sc)
471122999Smbr{
472140923Sscottl	ips_command_t *command;
473140923Sscottl
474140923Sscottl	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){
475122999Smbr		device_printf(sc->dev, "ERROR: unable to get a command! can't send ffdc reset!\n");
476122999Smbr	}
477140923Sscottl	ips_send_ffdc_reset_cmd(command);
478150535Sscottl	if(COMMAND_ERROR(command)){
479122999Smbr		device_printf(sc->dev, "ERROR: ffdc reset command failed!\n");
480122999Smbr	}
481122999Smbr	return 0;
482122999Smbr}
483122999Smbr
484114902Sscottlstatic void ips_write_nvram(ips_command_t *command){
485114902Sscottl	ips_softc_t *sc = command->sc;
486114902Sscottl	ips_rw_nvram_cmd *command_struct;
487114902Sscottl	ips_nvram_page5 *nvram;
488114902Sscottl
489114902Sscottl	/*FIXME check for error */
490114902Sscottl	command->callback = ips_wakeup_callback;
491114902Sscottl	command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
492114902Sscottl	command_struct->command = IPS_RW_NVRAM_CMD;
493114902Sscottl	command_struct->id = command->id;
494114902Sscottl	command_struct->pagenum = 5;
495114902Sscottl	command_struct->rw	= 1; /*write*/
496114902Sscottl	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
497114902Sscottl				BUS_DMASYNC_POSTREAD);
498114902Sscottl	nvram = command->data_buffer;
499122999Smbr	/* retrieve adapter info and save in sc */
500122999Smbr	sc->adapter_type = nvram->adapter_type;
501122999Smbr
502114902Sscottl	strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4);
503114902Sscottl	strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4);
504114902Sscottl	nvram->operating_system = IPS_OS_FREEBSD;
505114902Sscottl	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
506114902Sscottl			BUS_DMASYNC_PREWRITE);
507114902Sscottl	sc->ips_issue_cmd(command);
508114902Sscottl}
509114902Sscottl
510114902Sscottlstatic void ips_read_nvram_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
511114902Sscottl{
512114902Sscottl	ips_softc_t *sc;
513114902Sscottl	ips_command_t *command = cmdptr;
514114902Sscottl	ips_rw_nvram_cmd *command_struct;
515114902Sscottl	sc = command->sc;
516114902Sscottl	if(error){
517150535Sscottl		ips_set_error(command, error);
518114902Sscottl		printf("ips: error = %d in ips_read_nvram_callback\n", error);
519114902Sscottl		return;
520114902Sscottl	}
521114902Sscottl	command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
522114902Sscottl	command_struct->command = IPS_RW_NVRAM_CMD;
523114902Sscottl	command_struct->id = command->id;
524114902Sscottl	command_struct->pagenum = 5;
525114902Sscottl	command_struct->rw = 0;
526114902Sscottl	command_struct->buffaddr = segments[0].ds_addr;
527114902Sscottl
528114902Sscottl	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
529114902Sscottl			BUS_DMASYNC_PREWRITE);
530114902Sscottl	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
531114902Sscottl			BUS_DMASYNC_PREREAD);
532114902Sscottl	sc->ips_issue_cmd(command);
533150535Sscottl	if (sema_timedwait(&sc->cmd_sema, 30*hz) != 0) {
534150535Sscottl		ips_set_error(command, ETIMEDOUT);
535150535Sscottl		return;
536150535Sscottl	}
537150535Sscottl	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
538150535Sscottl			BUS_DMASYNC_POSTWRITE);
539114902Sscottl}
540114902Sscottl
541140923Sscottlstatic int ips_read_nvram(ips_command_t *command)
542140923Sscottl{
543114902Sscottl	int error = 0;
544114902Sscottl	ips_softc_t *sc = command->sc;
545114902Sscottl
546114902Sscottl	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
547114902Sscottl				/* alignemnt */	1,
548114902Sscottl				/* boundary  */	0,
549114902Sscottl				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
550114902Sscottl				/* highaddr  */	BUS_SPACE_MAXADDR,
551114902Sscottl				/* filter    */	NULL,
552114902Sscottl				/* filterarg */	NULL,
553114902Sscottl				/* maxsize   */	IPS_NVRAM_PAGE_SIZE,
554114902Sscottl				/* numsegs   */	1,
555114902Sscottl				/* maxsegsize*/	IPS_NVRAM_PAGE_SIZE,
556114902Sscottl				/* flags     */	0,
557140923Sscottl				/* lockfunc  */ NULL,
558140923Sscottl				/* lockarg   */ NULL,
559114902Sscottl				&command->data_dmatag) != 0) {
560114902Sscottl                printf("ips: can't alloc dma tag for nvram\n");
561114902Sscottl		error = ENOMEM;
562114902Sscottl		goto exit;
563114902Sscottl        }
564114902Sscottl	if(bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
565114902Sscottl	   		    BUS_DMA_NOWAIT, &command->data_dmamap)){
566114902Sscottl		error = ENOMEM;
567114902Sscottl		goto exit;
568114902Sscottl	}
569114902Sscottl	command->callback = ips_write_nvram;
570150535Sscottl	error = bus_dmamap_load(command->data_dmatag, command->data_dmamap,
571150535Sscottl				command->data_buffer,IPS_NVRAM_PAGE_SIZE,
572150535Sscottl				ips_read_nvram_callback, command,
573150535Sscottl				BUS_DMA_NOWAIT);
574150535Sscottl	if (error == 0)
575150535Sscottl		bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
576114902Sscottl
577114902Sscottlexit:
578114902Sscottl	bus_dmamem_free(command->data_dmatag, command->data_buffer,
579114902Sscottl			command->data_dmamap);
580114902Sscottl	bus_dma_tag_destroy(command->data_dmatag);
581114902Sscottl	ips_insert_free_cmd(sc, command);
582114902Sscottl	return error;
583114902Sscottl}
584114902Sscottl
585114902Sscottlint ips_update_nvram(ips_softc_t *sc)
586114902Sscottl{
587140923Sscottl	ips_command_t *command;
588140923Sscottl
589140923Sscottl	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){
590114902Sscottl		device_printf(sc->dev, "ERROR: unable to get a command! can't update nvram\n");
591114902Sscottl		return 1;
592114902Sscottl	}
593140923Sscottl	ips_read_nvram(command);
594150535Sscottl	if(COMMAND_ERROR(command)){
595114902Sscottl		device_printf(sc->dev, "ERROR: nvram update command failed!\n");
596114902Sscottl	}
597114902Sscottl	return 0;
598114902Sscottl
599114902Sscottl
600114902Sscottl}
601114902Sscottl
602114902Sscottl
603114902Sscottlstatic int ips_send_config_sync_cmd(ips_command_t *command)
604114902Sscottl{
605114902Sscottl	ips_softc_t *sc = command->sc;
606114902Sscottl	ips_generic_cmd *command_struct;
607114902Sscottl
608114902Sscottl	PRINTF(10,"ips test: got a command, building flush command\n");
609114902Sscottl	command->callback = ips_wakeup_callback;
610114902Sscottl	command_struct = (ips_generic_cmd *)command->command_buffer;
611114902Sscottl	command_struct->command = IPS_CONFIG_SYNC_CMD;
612114902Sscottl	command_struct->id = command->id;
613114902Sscottl	command_struct->reserve2 = IPS_POCL;
614114902Sscottl	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
615114902Sscottl			BUS_DMASYNC_PREWRITE);
616114902Sscottl	sc->ips_issue_cmd(command);
617150535Sscottl	if (COMMAND_ERROR(command) == 0)
618140923Sscottl		sema_wait(&sc->cmd_sema);
619114902Sscottl	ips_insert_free_cmd(sc, command);
620114902Sscottl	return 0;
621114902Sscottl}
622114902Sscottl
623114902Sscottlstatic int ips_send_error_table_cmd(ips_command_t *command)
624114902Sscottl{
625114902Sscottl	ips_softc_t *sc = command->sc;
626114902Sscottl	ips_generic_cmd *command_struct;
627114902Sscottl
628114902Sscottl	PRINTF(10,"ips test: got a command, building errortable command\n");
629114902Sscottl	command->callback = ips_wakeup_callback;
630114902Sscottl	command_struct = (ips_generic_cmd *)command->command_buffer;
631114902Sscottl	command_struct->command = IPS_ERROR_TABLE_CMD;
632114902Sscottl	command_struct->id = command->id;
633114902Sscottl	command_struct->reserve2 = IPS_CSL;
634114902Sscottl	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
635114902Sscottl			BUS_DMASYNC_PREWRITE);
636114902Sscottl	sc->ips_issue_cmd(command);
637150535Sscottl	if (COMMAND_ERROR(command) == 0)
638140923Sscottl		sema_wait(&sc->cmd_sema);
639114902Sscottl	ips_insert_free_cmd(sc, command);
640114902Sscottl	return 0;
641114902Sscottl}
642114902Sscottl
643114902Sscottl
644114902Sscottlint ips_clear_adapter(ips_softc_t *sc)
645114902Sscottl{
646140923Sscottl	ips_command_t *command;
647140923Sscottl
648114902Sscottl	device_printf(sc->dev, "syncing config\n");
649140923Sscottl	if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){
650114902Sscottl		device_printf(sc->dev, "ERROR: unable to get a command! can't sync cache!\n");
651114902Sscottl		return 1;
652114902Sscottl	}
653140923Sscottl	ips_send_config_sync_cmd(command);
654150535Sscottl	if(COMMAND_ERROR(command)){
655114902Sscottl		device_printf(sc->dev, "ERROR: cache sync command failed!\n");
656114902Sscottl		return 1;
657114902Sscottl	}
658114902Sscottl
659114902Sscottl	device_printf(sc->dev, "clearing error table\n");
660140923Sscottl	if(ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){
661114902Sscottl		device_printf(sc->dev, "ERROR: unable to get a command! can't sync cache!\n");
662114902Sscottl		return 1;
663114902Sscottl	}
664140923Sscottl	ips_send_error_table_cmd(command);
665150535Sscottl	if(COMMAND_ERROR(command)){
666114902Sscottl		device_printf(sc->dev, "ERROR: etable command failed!\n");
667114902Sscottl		return 1;
668114902Sscottl	}
669114902Sscottl
670114902Sscottl	return 0;
671114902Sscottl}
672