xref: /freebsd/sys/dev/ips/ips.c (revision 4e62c3cafa4c4e41efd6f87b7fe559cf819cf3e4)
12aedd662SScott Long /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4aad970f1SDavid E. O'Brien  * Written by: David Jeffery
52aedd662SScott Long  * Copyright (c) 2002 Adaptec Inc.
62aedd662SScott Long  * All rights reserved.
72aedd662SScott Long  *
82aedd662SScott Long  * Redistribution and use in source and binary forms, with or without
92aedd662SScott Long  * modification, are permitted provided that the following conditions
102aedd662SScott Long  * are met:
112aedd662SScott Long  * 1. Redistributions of source code must retain the above copyright
122aedd662SScott Long  *    notice, this list of conditions and the following disclaimer.
132aedd662SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
142aedd662SScott Long  *    notice, this list of conditions and the following disclaimer in the
152aedd662SScott Long  *    documentation and/or other materials provided with the distribution.
162aedd662SScott Long  *
172aedd662SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
182aedd662SScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
192aedd662SScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
202aedd662SScott Long  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
212aedd662SScott Long  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
222aedd662SScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
232aedd662SScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
242aedd662SScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
252aedd662SScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
262aedd662SScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
272aedd662SScott Long  * SUCH DAMAGE.
282aedd662SScott Long  */
292aedd662SScott Long 
30e2e050c8SConrad Meyer #include <sys/types.h>
31e2e050c8SConrad Meyer #include <sys/lock.h>
32e2e050c8SConrad Meyer #include <sys/mutex.h>
332aedd662SScott Long #include <sys/stat.h>
342aedd662SScott Long #include <sys/time.h>
352aedd662SScott Long 
36e2e050c8SConrad Meyer #include <dev/ips/ipsreg.h>
37e2e050c8SConrad Meyer #include <dev/ips/ips.h>
38e2e050c8SConrad Meyer 
392aedd662SScott Long static d_open_t ips_open;
402aedd662SScott Long static d_close_t ips_close;
412aedd662SScott Long static d_ioctl_t ips_ioctl;
422aedd662SScott Long 
43dd83a01eSScott Long MALLOC_DEFINE(M_IPSBUF, "ipsbuf","IPS driver buffer");
44dd83a01eSScott Long 
452aedd662SScott Long static struct cdevsw ips_cdevsw = {
46dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
472aedd662SScott Long 	.d_open =	ips_open,
482aedd662SScott Long 	.d_close =	ips_close,
492aedd662SScott Long 	.d_ioctl =	ips_ioctl,
502aedd662SScott Long 	.d_name =	"ips",
512aedd662SScott Long };
522aedd662SScott Long 
537633e7f1SMartin Blapp static const char* ips_adapter_name[] = {
547633e7f1SMartin Blapp 	"N/A",
557633e7f1SMartin Blapp 	"ServeRAID (copperhead)",
567633e7f1SMartin Blapp 	"ServeRAID II (copperhead refresh)",
577633e7f1SMartin Blapp 	"ServeRAID onboard (copperhead)",
587633e7f1SMartin Blapp 	"ServeRAID onboard (copperhead)",
597633e7f1SMartin Blapp 	"ServeRAID 3H (clarinet)",
607633e7f1SMartin Blapp 	"ServeRAID 3L (clarinet lite)",
617633e7f1SMartin Blapp 	"ServeRAID 4H (trombone)",
627633e7f1SMartin Blapp 	"ServeRAID 4M (morpheus)",
637633e7f1SMartin Blapp 	"ServeRAID 4L (morpheus lite)",
647633e7f1SMartin Blapp 	"ServeRAID 4Mx (neo)",
657633e7f1SMartin Blapp 	"ServeRAID 4Lx (neo lite)",
667633e7f1SMartin Blapp 	"ServeRAID 5i II (sarasota)",
677633e7f1SMartin Blapp 	"ServeRAID 5i (sarasota)",
687633e7f1SMartin Blapp 	"ServeRAID 6M (marco)",
69bd4fb874SMaxim Konovalov 	"ServeRAID 6i (sebring)",
70bd4fb874SMaxim Konovalov 	"ServeRAID 7t",
71bd4fb874SMaxim Konovalov 	"ServeRAID 7k",
72bd4fb874SMaxim Konovalov 	"ServeRAID 7M"
737633e7f1SMartin Blapp };
747633e7f1SMartin Blapp 
752aedd662SScott Long 
ips_open(struct cdev * dev,int flags,int fmt,struct thread * td)7689c9c53dSPoul-Henning Kamp static int ips_open(struct cdev *dev, int flags, int fmt, struct thread *td)
772aedd662SScott Long {
782aedd662SScott Long 	ips_softc_t *sc = dev->si_drv1;
79352176c8SJohn Baldwin 	mtx_lock(&sc->queue_mtx);
802aedd662SScott Long 	sc->state |= IPS_DEV_OPEN;
81352176c8SJohn Baldwin 	mtx_unlock(&sc->queue_mtx);
822aedd662SScott Long         return 0;
832aedd662SScott Long }
842aedd662SScott Long 
ips_close(struct cdev * dev,int flags,int fmt,struct thread * td)8589c9c53dSPoul-Henning Kamp static int ips_close(struct cdev *dev, int flags, int fmt, struct thread *td)
862aedd662SScott Long {
872aedd662SScott Long 	ips_softc_t *sc = dev->si_drv1;
88352176c8SJohn Baldwin 
89352176c8SJohn Baldwin 	mtx_lock(&sc->queue_mtx);
902aedd662SScott Long 	sc->state &= ~IPS_DEV_OPEN;
91352176c8SJohn Baldwin 	mtx_unlock(&sc->queue_mtx);
922aedd662SScott Long 
932aedd662SScott Long         return 0;
942aedd662SScott Long }
952aedd662SScott Long 
ips_ioctl(struct cdev * dev,u_long command,caddr_t addr,int32_t flags,struct thread * td)9689c9c53dSPoul-Henning Kamp static int ips_ioctl(struct cdev *dev, u_long command, caddr_t addr, int32_t flags, struct thread *td)
972aedd662SScott Long {
982aedd662SScott Long 	ips_softc_t *sc;
992aedd662SScott Long 
1002aedd662SScott Long 	sc = dev->si_drv1;
1012aedd662SScott Long 	return ips_ioctl_request(sc, command, addr, flags);
1022aedd662SScott Long }
1032aedd662SScott Long 
ips_cmd_dmaload(void * cmdptr,bus_dma_segment_t * segments,int segnum,int error)1042aedd662SScott Long static void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
1052aedd662SScott Long {
1062aedd662SScott Long 	ips_command_t *command = cmdptr;
1072aedd662SScott Long 	PRINTF(10, "ips: in ips_cmd_dmaload\n");
1082aedd662SScott Long 	if(!error)
1092aedd662SScott Long 		command->command_phys_addr = segments[0].ds_addr;
1102aedd662SScott Long 
1112aedd662SScott Long }
1122aedd662SScott Long 
113453130d9SPedro F. Giffuni /* is locking needed? what locking guarantees are there on removal? */
ips_cmdqueue_free(ips_softc_t * sc)11403a908f2SScott Long static int ips_cmdqueue_free(ips_softc_t *sc)
1152aedd662SScott Long {
1162aedd662SScott Long 	int i, error = -1;
117b234a120SScott Long 	ips_command_t *command;
118b234a120SScott Long 
1192aedd662SScott Long 	if(!sc->used_commands){
1202aedd662SScott Long 		for(i = 0; i < sc->max_cmds; i++){
121b234a120SScott Long 
122b234a120SScott Long 			command = &sc->commandarray[i];
123b234a120SScott Long 
124b234a120SScott Long 			if(command->command_phys_addr == 0)
1252aedd662SScott Long 				continue;
1262aedd662SScott Long 			bus_dmamap_unload(sc->command_dmatag,
127b234a120SScott Long 					  command->command_dmamap);
1282aedd662SScott Long 			bus_dmamem_free(sc->command_dmatag,
129b234a120SScott Long 					command->command_buffer,
130b234a120SScott Long 					command->command_dmamap);
13103a908f2SScott Long 			if (command->data_dmamap != NULL)
13203a908f2SScott Long 				bus_dmamap_destroy(command->data_dmatag,
13303a908f2SScott Long 				    command->data_dmamap);
1342aedd662SScott Long 		}
1352aedd662SScott Long 		error = 0;
1362aedd662SScott Long 		sc->state |= IPS_OFFLINE;
1372aedd662SScott Long 	}
13803a908f2SScott Long 	sc->staticcmd = NULL;
13903a908f2SScott Long 	free(sc->commandarray, M_DEVBUF);
1402aedd662SScott Long 	return error;
1412aedd662SScott Long }
1422aedd662SScott Long 
1432aedd662SScott Long /* places all ips command structs on the free command queue.  No locking as if someone else tries
1442aedd662SScott Long  * to access this during init, we have bigger problems */
ips_cmdqueue_init(ips_softc_t * sc)14503a908f2SScott Long static int ips_cmdqueue_init(ips_softc_t *sc)
1462aedd662SScott Long {
1472aedd662SScott Long 	int i;
1482aedd662SScott Long 	ips_command_t *command;
14903a908f2SScott Long 
15003a908f2SScott Long 	sc->commandarray = (ips_command_t *)malloc(sizeof(ips_command_t) *
15103a908f2SScott Long 	    sc->max_cmds, M_DEVBUF, M_NOWAIT|M_ZERO);
15203a908f2SScott Long 	if (sc->commandarray == NULL)
15303a908f2SScott Long 		return (ENOMEM);
15403a908f2SScott Long 
1552aedd662SScott Long 	SLIST_INIT(&sc->free_cmd_list);
1562aedd662SScott Long 	for(i = 0; i < sc->max_cmds; i++){
1572aedd662SScott Long 		command = &sc->commandarray[i];
158b234a120SScott Long 		command->id = i;
159b234a120SScott Long 		command->sc = sc;
160b234a120SScott Long 
1612aedd662SScott Long 		if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer,
1622aedd662SScott Long 		    BUS_DMA_NOWAIT, &command->command_dmamap))
1632aedd662SScott Long 			goto error;
1642aedd662SScott Long 		bus_dmamap_load(sc->command_dmatag, command->command_dmamap,
1652aedd662SScott Long 				command->command_buffer,IPS_COMMAND_LEN,
1662aedd662SScott Long 				ips_cmd_dmaload, command, BUS_DMA_NOWAIT);
1672aedd662SScott Long 		if(!command->command_phys_addr){
1682aedd662SScott Long 			bus_dmamem_free(sc->command_dmatag,
1692aedd662SScott Long 			    command->command_buffer, command->command_dmamap);
1702aedd662SScott Long 			goto error;
1712aedd662SScott Long 		}
172b234a120SScott Long 
17303a908f2SScott Long 		if (i != 0) {
17403a908f2SScott Long 			command->data_dmatag = sc->sg_dmatag;
17503a908f2SScott Long 			if (bus_dmamap_create(command->data_dmatag, 0,
17603a908f2SScott Long 			    &command->data_dmamap))
17703a908f2SScott Long 				goto error;
178b234a120SScott Long 			SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
17903a908f2SScott Long 		} else
18003a908f2SScott Long 			sc->staticcmd = command;
1812aedd662SScott Long 	}
1822aedd662SScott Long 	sc->state &= ~IPS_OFFLINE;
1832aedd662SScott Long 	return 0;
1842aedd662SScott Long error:
1852aedd662SScott Long 	ips_cmdqueue_free(sc);
1862aedd662SScott Long 	return ENOMEM;
1872aedd662SScott Long }
1882aedd662SScott Long 
1892aedd662SScott Long /* returns a free command struct if one is available.
1902aedd662SScott Long  * It also blanks out anything that may be a wild pointer/value.
1912aedd662SScott Long  * Also, command buffers are not freed.  They are
1922aedd662SScott Long  * small so they are saved and kept dmamapped and loaded.
1932aedd662SScott Long  */
ips_get_free_cmd(ips_softc_t * sc,ips_command_t ** cmd,unsigned long flags)19403a908f2SScott Long int ips_get_free_cmd(ips_softc_t *sc, ips_command_t **cmd, unsigned long flags)
1952aedd662SScott Long {
1962aedd662SScott Long 	ips_command_t *command;
1972aedd662SScott Long 
1982aedd662SScott Long 	if(sc->state & IPS_OFFLINE){
1992aedd662SScott Long 		return EIO;
2002aedd662SScott Long 	}
20103a908f2SScott Long 	if ((flags & IPS_STATIC_FLAG) == 0) {
2022aedd662SScott Long 		command = SLIST_FIRST(&sc->free_cmd_list);
2032aedd662SScott Long 		if(!command || (sc->state & IPS_TIMEOUT)){
20403a908f2SScott Long 			return EBUSY;
2052aedd662SScott Long 		}
2062aedd662SScott Long 		SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
2072aedd662SScott Long 		(sc->used_commands)++;
20803a908f2SScott Long 	} else {
20903a908f2SScott Long 		if (sc->state & IPS_STATIC_BUSY)
21003a908f2SScott Long 			return EAGAIN;
21103a908f2SScott Long 		command = sc->staticcmd;
21203a908f2SScott Long 		sc->state |= IPS_STATIC_BUSY;
21303a908f2SScott Long 	}
2142aedd662SScott Long 	clear_ips_command(command);
2152aedd662SScott Long 	bzero(command->command_buffer, IPS_COMMAND_LEN);
21603a908f2SScott Long 	*cmd = command;
21703a908f2SScott Long 	return 0;
2182aedd662SScott Long }
2192aedd662SScott Long 
2202aedd662SScott Long /* adds a command back to the free command queue */
ips_insert_free_cmd(ips_softc_t * sc,ips_command_t * command)2212aedd662SScott Long void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command)
2222aedd662SScott Long {
223b234a120SScott Long 
22403a908f2SScott Long 	if (sema_value(&sc->cmd_sema) != 0)
225b234a120SScott Long 		panic("ips: command returned non-zero semaphore");
226b234a120SScott Long 
22703a908f2SScott Long 	if (command != sc->staticcmd) {
2282aedd662SScott Long 		SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
2292aedd662SScott Long 		(sc->used_commands)--;
23003a908f2SScott Long 	} else {
23103a908f2SScott Long 		sc->state &= ~IPS_STATIC_BUSY;
23203a908f2SScott Long 	}
2332aedd662SScott Long }
ips_diskdev_statename(u_int8_t state)2347633e7f1SMartin Blapp static const char* ips_diskdev_statename(u_int8_t state)
2357633e7f1SMartin Blapp {
2367633e7f1SMartin Blapp 	static char statebuf[20];
2377633e7f1SMartin Blapp 	switch(state){
2387633e7f1SMartin Blapp 		case IPS_LD_OFFLINE:
2397633e7f1SMartin Blapp 			return("OFFLINE");
2407633e7f1SMartin Blapp 			break;
2417633e7f1SMartin Blapp 		case IPS_LD_OKAY:
2427633e7f1SMartin Blapp 			return("OK");
2437633e7f1SMartin Blapp 			break;
2447633e7f1SMartin Blapp 		case IPS_LD_DEGRADED:
2457633e7f1SMartin Blapp 			return("DEGRADED");
2467633e7f1SMartin Blapp 			break;
2477633e7f1SMartin Blapp 		case IPS_LD_FREE:
2487633e7f1SMartin Blapp 			return("FREE");
2497633e7f1SMartin Blapp 			break;
2507633e7f1SMartin Blapp 		case IPS_LD_SYS:
2517633e7f1SMartin Blapp 			return("SYS");
2527633e7f1SMartin Blapp 			break;
2537633e7f1SMartin Blapp 		case IPS_LD_CRS:
2547633e7f1SMartin Blapp 			return("CRS");
2557633e7f1SMartin Blapp 			break;
2567633e7f1SMartin Blapp 	}
2577633e7f1SMartin Blapp 	sprintf(statebuf,"UNKNOWN(0x%02x)", state);
2587633e7f1SMartin Blapp 	return(statebuf);
2597633e7f1SMartin Blapp }
2602aedd662SScott Long 
ips_diskdev_init(ips_softc_t * sc)2612aedd662SScott Long static int ips_diskdev_init(ips_softc_t *sc)
2622aedd662SScott Long {
2632aedd662SScott Long 	int i;
2642aedd662SScott Long 	for(i=0; i < IPS_MAX_NUM_DRIVES; i++){
2657633e7f1SMartin Blapp 		if(sc->drives[i].state == IPS_LD_FREE) continue;
2667633e7f1SMartin Blapp 		device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n",
2677633e7f1SMartin Blapp 			i, sc->drives[i].raid_lvl,
2687633e7f1SMartin Blapp 			sc->drives[i].sector_count,
2697633e7f1SMartin Blapp 			ips_diskdev_statename(sc->drives[i].state));
2707633e7f1SMartin Blapp 		if(sc->drives[i].state == IPS_LD_OKAY ||
2717633e7f1SMartin Blapp 		   sc->drives[i].state == IPS_LD_DEGRADED){
2725b56413dSWarner Losh 			sc->diskdev[i] = device_add_child(sc->dev, NULL, DEVICE_UNIT_ANY);
273f472527cSPeter Wemm 			device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i);
2742aedd662SScott Long 		}
2752aedd662SScott Long 	}
27618250ec6SJohn Baldwin 	bus_attach_children(sc->dev);
2772aedd662SScott Long 	return 0;
2782aedd662SScott Long }
2792aedd662SScott Long 
ips_diskdev_free(ips_softc_t * sc)2802aedd662SScott Long static int ips_diskdev_free(ips_softc_t *sc)
2812aedd662SScott Long {
282*4e62c3caSJohn Baldwin 	return (bus_generic_detach(sc->dev));
2832aedd662SScott Long }
2842aedd662SScott Long 
2852aedd662SScott Long /* ips_timeout is periodically called to make sure no commands sent
2862aedd662SScott Long  * to the card have become stuck.  If it finds a stuck command, it
2872aedd662SScott Long  * sets a flag so the driver won't start any more commands and then
2882aedd662SScott Long  * is periodically called to see if all outstanding commands have
2892aedd662SScott Long  * either finished or timed out.  Once timed out, an attempt to
2902aedd662SScott Long  * reinitialize the card is made.  If that fails, the driver gives
2912aedd662SScott Long  * up and declares the card dead. */
ips_timeout(void * arg)2922aedd662SScott Long static void ips_timeout(void *arg)
2932aedd662SScott Long {
2942aedd662SScott Long 	ips_softc_t *sc = arg;
2952aedd662SScott Long 	int i, state = 0;
2962aedd662SScott Long 	ips_command_t *command;
29703a908f2SScott Long 
298352176c8SJohn Baldwin 	mtx_assert(&sc->queue_mtx, MA_OWNED);
2992aedd662SScott Long 	command = &sc->commandarray[0];
3002aedd662SScott Long 	for(i = 0; i < sc->max_cmds; i++){
3012aedd662SScott Long 		if(!command[i].timeout){
3022aedd662SScott Long 			continue;
3032aedd662SScott Long 		}
3042aedd662SScott Long 		command[i].timeout--;
3052aedd662SScott Long 		if(!command[i].timeout){
3062aedd662SScott Long 			if(!(sc->state & IPS_TIMEOUT)){
3072aedd662SScott Long 				sc->state |= IPS_TIMEOUT;
3082aedd662SScott Long 				device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n");
3092aedd662SScott Long 			}
3102eea7051SScott Long 			ips_set_error(&command[i], ETIMEDOUT);
3112aedd662SScott Long 			command[i].callback(&command[i]);
3122aedd662SScott Long 			/* hmm, this should be enough cleanup */
3132aedd662SScott Long 		} else
3142aedd662SScott Long 			state = 1;
3152aedd662SScott Long 	}
3162aedd662SScott Long 	if(!state && (sc->state & IPS_TIMEOUT)){
3172aedd662SScott Long 		if(sc->ips_adapter_reinit(sc, 1)){
3182aedd662SScott Long 			device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n");
3192aedd662SScott Long 			sc->state |= IPS_OFFLINE;
3202aedd662SScott Long 			sc->state &= ~IPS_TIMEOUT;
3212aedd662SScott Long 			/* Grr, I hate this solution. I run waiting commands
3222aedd662SScott Long 			   one at a time and error them out just before they
3232aedd662SScott Long 			   would go to the card. This sucks. */
3242aedd662SScott Long 		} else
3252aedd662SScott Long 			sc->state &= ~IPS_TIMEOUT;
3262aedd662SScott Long 	}
3272aedd662SScott Long 	if (sc->state != IPS_OFFLINE)
328352176c8SJohn Baldwin 		callout_reset(&sc->timer, 10 * hz, ips_timeout, sc);
3292aedd662SScott Long }
3302aedd662SScott Long 
3312aedd662SScott Long /* check card and initialize it */
ips_adapter_init(ips_softc_t * sc)3322aedd662SScott Long int ips_adapter_init(ips_softc_t *sc)
3332aedd662SScott Long {
334dea4622dSScott Long         int i;
3352aedd662SScott Long         DEVICE_PRINTF(1,sc->dev, "initializing\n");
336b234a120SScott Long 
3372aedd662SScott Long         if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
3382aedd662SScott Long 				/* alignemnt */	1,
3392aedd662SScott Long 				/* boundary  */	0,
3402aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
3412aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
3422aedd662SScott Long 				/* filter    */	NULL,
3432aedd662SScott Long 				/* filterarg */	NULL,
3442aedd662SScott Long 				/* maxsize   */	IPS_COMMAND_LEN +
3452aedd662SScott Long 						    IPS_MAX_SG_LEN,
3462aedd662SScott Long 				/* numsegs   */	1,
3472aedd662SScott Long 				/* maxsegsize*/	IPS_COMMAND_LEN +
3482aedd662SScott Long 						    IPS_MAX_SG_LEN,
3492aedd662SScott Long 				/* flags     */	0,
35003a908f2SScott Long 				/* lockfunc  */ NULL,
35103a908f2SScott Long 				/* lockarg   */ NULL,
3522aedd662SScott Long 				&sc->command_dmatag) != 0) {
3532aedd662SScott Long                 device_printf(sc->dev, "can't alloc command dma tag\n");
3542aedd662SScott Long 		goto error;
3552aedd662SScott Long         }
3562aedd662SScott Long 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
3572aedd662SScott Long 				/* alignemnt */	1,
3582aedd662SScott Long 				/* boundary  */	0,
3592aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
3602aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
3612aedd662SScott Long 				/* filter    */	NULL,
3622aedd662SScott Long 				/* filterarg */	NULL,
3632aedd662SScott Long 				/* maxsize   */	IPS_MAX_IOBUF_SIZE,
3642aedd662SScott Long 				/* numsegs   */	IPS_MAX_SG_ELEMENTS,
3652aedd662SScott Long 				/* maxsegsize*/	IPS_MAX_IOBUF_SIZE,
3662aedd662SScott Long 				/* flags     */	0,
367f6b1c44dSScott Long 				/* lockfunc  */ busdma_lock_mutex,
36803a908f2SScott Long 				/* lockarg   */ &sc->queue_mtx,
3692aedd662SScott Long 				&sc->sg_dmatag) != 0) {
3702aedd662SScott Long 		device_printf(sc->dev, "can't alloc SG dma tag\n");
3712aedd662SScott Long 		goto error;
3722aedd662SScott Long 	}
3732aedd662SScott Long 	/* create one command buffer until we know how many commands this card
3742aedd662SScott Long            can handle */
3752aedd662SScott Long 	sc->max_cmds = 1;
3762aedd662SScott Long 	ips_cmdqueue_init(sc);
3772aedd662SScott Long 
3782aedd662SScott Long 	if(sc->ips_adapter_reinit(sc, 0))
3792aedd662SScott Long 		goto error;
3802aedd662SScott Long 
3817633e7f1SMartin Blapp 	/* initialize ffdc values */
3827633e7f1SMartin Blapp 	microtime(&sc->ffdc_resettime);
3837633e7f1SMartin Blapp 	sc->ffdc_resetcount = 1;
3847633e7f1SMartin Blapp 	if ((i = ips_ffdc_reset(sc)) != 0) {
3857633e7f1SMartin Blapp 		device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i);
3867633e7f1SMartin Blapp 		goto error;
3877633e7f1SMartin Blapp 	}
388dea4622dSScott Long 	if ((i = ips_get_adapter_info(sc)) != 0) {
389dea4622dSScott Long 		device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i);
390dea4622dSScott Long 		goto error;
391dea4622dSScott Long 	}
3927633e7f1SMartin Blapp 	ips_update_nvram(sc); /* no error check as failure doesn't matter */
3937633e7f1SMartin Blapp 	if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){
3947633e7f1SMartin Blapp 		device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]);
3957633e7f1SMartin Blapp 	}
396dea4622dSScott Long  	if ((i = ips_get_drive_info(sc)) != 0) {
397dea4622dSScott Long 		device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i);
3982aedd662SScott Long 		goto error;
3992aedd662SScott Long 	}
4002aedd662SScott Long 
4012aedd662SScott Long         ips_cmdqueue_free(sc);
4022aedd662SScott Long 	if(sc->adapter_info.max_concurrent_cmds)
4032aedd662SScott Long         	sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds);
4042aedd662SScott Long 	else
4052aedd662SScott Long 		sc->max_cmds = 32;
4062aedd662SScott Long         if(ips_cmdqueue_init(sc)){
4072aedd662SScott Long 		device_printf(sc->dev, "failed to initialize command buffers\n");
4082aedd662SScott Long 		goto error;
4092aedd662SScott Long 	}
4102aedd662SScott Long         sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR,
4112aedd662SScott Long                                         S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev));
4122aedd662SScott Long 	sc->device_file->si_drv1 = sc;
4132aedd662SScott Long 	ips_diskdev_init(sc);
414352176c8SJohn Baldwin 	callout_reset(&sc->timer, 10 * hz, ips_timeout, sc);
4152aedd662SScott Long         return 0;
4162aedd662SScott Long 
4172aedd662SScott Long error:
4182aedd662SScott Long 	ips_adapter_free(sc);
4192aedd662SScott Long 	return ENXIO;
4202aedd662SScott Long }
4212aedd662SScott Long 
4222aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization */
ips_morpheus_reinit(ips_softc_t * sc,int force)4232aedd662SScott Long int ips_morpheus_reinit(ips_softc_t *sc, int force)
4242aedd662SScott Long {
4252aedd662SScott Long         u_int32_t tmp;
4262aedd662SScott Long 	int i;
4272aedd662SScott Long 
4282aedd662SScott Long 	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4292aedd662SScott Long 	if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) &&
4302aedd662SScott Long 	    (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){
4312aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
4322aedd662SScott Long 		return 0;
4332aedd662SScott Long 	}
4342aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff);
4352aedd662SScott Long 	ips_read_4(sc, MORPHEUS_REG_OIMR);
4362aedd662SScott Long 
4372aedd662SScott Long 	device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n");
4382aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000);
4392aedd662SScott Long 	DELAY(5000000);
440f94dfeb4SScott Long 	ips_read_4(sc, MORPHEUS_REG_OIMR);
4412aedd662SScott Long 
4422aedd662SScott Long 	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4432aedd662SScott Long 	for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){
4442aedd662SScott Long 		DELAY(1000000);
4452aedd662SScott Long 		DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i);
4462aedd662SScott Long 		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4472aedd662SScott Long 	}
4482aedd662SScott Long 	if(tmp & MORPHEUS_BIT_POST1)
4492aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1);
4502aedd662SScott Long 
4512aedd662SScott Long         if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){
4522aedd662SScott Long                 device_printf(sc->dev,"Adapter error during initialization.\n");
4532aedd662SScott Long 		return 1;
4542aedd662SScott Long         }
4552aedd662SScott Long 	for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){
4562aedd662SScott Long 		DELAY(1000000);
4572aedd662SScott Long 		DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i);
4582aedd662SScott Long 		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4592aedd662SScott Long 	}
4602aedd662SScott Long 	if(tmp & MORPHEUS_BIT_POST2)
4612aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2);
4622aedd662SScott Long 
4632aedd662SScott Long 	if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){
4642aedd662SScott Long 		device_printf(sc->dev, "adapter failed config check\n");
4652aedd662SScott Long 		return 1;
4662aedd662SScott Long         }
4672aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
4682aedd662SScott Long 	if(force && ips_clear_adapter(sc)){
4692aedd662SScott Long 		device_printf(sc->dev, "adapter clear failed\n");
4702aedd662SScott Long 		return 1;
4712aedd662SScott Long 	}
4722aedd662SScott Long 	return 0;
4732aedd662SScott Long }
4742aedd662SScott Long 
4752aedd662SScott Long /* clean up so we can unload the driver. */
ips_adapter_free(ips_softc_t * sc)4762aedd662SScott Long int ips_adapter_free(ips_softc_t *sc)
4772aedd662SScott Long {
4782aedd662SScott Long 	int error = 0;
4792aedd662SScott Long 	if(sc->state & IPS_DEV_OPEN)
4802aedd662SScott Long 		return EBUSY;
4812aedd662SScott Long 	if((error = ips_diskdev_free(sc)))
4822aedd662SScott Long 		return error;
4832aedd662SScott Long 	if(ips_cmdqueue_free(sc)){
4842aedd662SScott Long 		device_printf(sc->dev,
4852aedd662SScott Long 		     "trying to exit when command queue is not empty!\n");
4862aedd662SScott Long 		return EBUSY;
4872aedd662SScott Long 	}
4882aedd662SScott Long 	DEVICE_PRINTF(1, sc->dev, "free\n");
489352176c8SJohn Baldwin 	callout_drain(&sc->timer);
4902aedd662SScott Long 
4912aedd662SScott Long 	if(sc->sg_dmatag)
4922aedd662SScott Long 		bus_dma_tag_destroy(sc->sg_dmatag);
4932aedd662SScott Long 	if(sc->command_dmatag)
4942aedd662SScott Long 		bus_dma_tag_destroy(sc->command_dmatag);
4952aedd662SScott Long 	if(sc->device_file)
4962aedd662SScott Long 	        destroy_dev(sc->device_file);
4972aedd662SScott Long         return 0;
4982aedd662SScott Long }
4992aedd662SScott Long 
ips_morpheus_check_intr(ips_softc_t * sc)5007765040eSScott Long static __inline int ips_morpheus_check_intr(ips_softc_t *sc)
5012aedd662SScott Long {
5022aedd662SScott Long 	int cmdnumber;
5032aedd662SScott Long 	ips_cmd_status_t status;
5047765040eSScott Long 	ips_command_t *command;
5057765040eSScott Long 	int found = 0;
5067765040eSScott Long 	u_int32_t oisr;
5072aedd662SScott Long 
5082aedd662SScott Long 	oisr = ips_read_4(sc, MORPHEUS_REG_OISR);
5097765040eSScott Long 	PRINTF(9, "interrupt registers out:%x\n", oisr);
5102aedd662SScott Long 	if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){
5112aedd662SScott Long 		DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n");
5127765040eSScott Long 		return (0);
5132aedd662SScott Long 	}
5142aedd662SScott Long 	while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){
5152aedd662SScott Long 		cmdnumber = status.fields.command_id;
5167765040eSScott Long 		command = &sc->commandarray[cmdnumber];
5177765040eSScott Long 		command->status.value = status.value;
5187765040eSScott Long 		command->timeout = 0;
5197765040eSScott Long 		command->callback(command);
5202aedd662SScott Long 
5217765040eSScott Long 		found = 1;
5222aedd662SScott Long 	}
5237765040eSScott Long         return (found);
5247765040eSScott Long }
5257765040eSScott Long 
ips_morpheus_intr(void * void_sc)5267765040eSScott Long void ips_morpheus_intr(void *void_sc)
5277765040eSScott Long {
5287765040eSScott Long 	ips_softc_t *sc = void_sc;
5297765040eSScott Long 
5307765040eSScott Long 	mtx_lock(&sc->queue_mtx);
5317765040eSScott Long 	ips_morpheus_check_intr(sc);
53203a908f2SScott Long 	mtx_unlock(&sc->queue_mtx);
5337765040eSScott Long }
5347765040eSScott Long 
ips_morpheus_poll(ips_command_t * command)5357765040eSScott Long void ips_morpheus_poll(ips_command_t *command)
5367765040eSScott Long {
5377765040eSScott Long 	uint32_t ts;
5387765040eSScott Long 
5394ef63badSScott Long 	/*
5404ef63badSScott Long 	 * Locks are not used here because this is only called during
5414ef63badSScott Long 	 * crashdumps.
5424ef63badSScott Long 	 */
5437765040eSScott Long 	ts = time_second + command->timeout;
5447765040eSScott Long 	while ((command->timeout != 0)
5457765040eSScott Long 	 && (ips_morpheus_check_intr(command->sc) == 0)
5467765040eSScott Long 	 && (ts > time_second))
5477765040eSScott Long 		DELAY(1000);
5482aedd662SScott Long }
5492aedd662SScott Long 
ips_issue_morpheus_cmd(ips_command_t * command)5502aedd662SScott Long void ips_issue_morpheus_cmd(ips_command_t *command)
5512aedd662SScott Long {
5522aedd662SScott Long 	/* hmmm, is there a cleaner way to do this? */
5532aedd662SScott Long 	if(command->sc->state & IPS_OFFLINE){
5542eea7051SScott Long 		ips_set_error(command, EINVAL);
5552aedd662SScott Long 		command->callback(command);
5562aedd662SScott Long 		return;
5572aedd662SScott Long 	}
5582aedd662SScott Long 	command->timeout = 10;
5592aedd662SScott Long 	ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr);
5602aedd662SScott Long }
5612aedd662SScott Long 
ips_copperhead_queue_callback(void * queueptr,bus_dma_segment_t * segments,int segnum,int error)5622aedd662SScott Long static void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error)
5632aedd662SScott Long {
5642aedd662SScott Long 	ips_copper_queue_t *queue = queueptr;
5652aedd662SScott Long 	if(error){
5662aedd662SScott Long 		return;
5672aedd662SScott Long 	}
5682aedd662SScott Long 	queue->base_phys_addr = segments[0].ds_addr;
5692aedd662SScott Long }
5702aedd662SScott Long 
ips_copperhead_queue_init(ips_softc_t * sc)5712aedd662SScott Long static int ips_copperhead_queue_init(ips_softc_t *sc)
5722aedd662SScott Long {
5732aedd662SScott Long 	int error;
5742aedd662SScott Long 	bus_dma_tag_t dmatag;
57576fe16bbSHiren Panchasara 	bus_dmamap_t dmamap;
5762aedd662SScott Long        	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
5772aedd662SScott Long 				/* alignemnt */	1,
5782aedd662SScott Long 				/* boundary  */	0,
5792aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
5802aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
5812aedd662SScott Long 				/* filter    */	NULL,
5822aedd662SScott Long 				/* filterarg */	NULL,
5832aedd662SScott Long 				/* maxsize   */	sizeof(ips_copper_queue_t),
5842aedd662SScott Long 				/* numsegs   */	1,
5852aedd662SScott Long 				/* maxsegsize*/	sizeof(ips_copper_queue_t),
5862aedd662SScott Long 				/* flags     */	0,
58703a908f2SScott Long 				/* lockfunc  */ NULL,
58803a908f2SScott Long 				/* lockarg   */ NULL,
5892aedd662SScott Long 				&dmatag) != 0) {
5902aedd662SScott Long                 device_printf(sc->dev, "can't alloc dma tag for statue queue\n");
5912aedd662SScott Long 		error = ENOMEM;
59276fe16bbSHiren Panchasara 		return error;
5932aedd662SScott Long         }
5942aedd662SScott Long 	if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue),
5952aedd662SScott Long 	   		    BUS_DMA_NOWAIT, &dmamap)){
5962aedd662SScott Long 		error = ENOMEM;
5972aedd662SScott Long 		goto exit;
5982aedd662SScott Long 	}
5992aedd662SScott Long 	bzero(sc->copper_queue, sizeof(ips_copper_queue_t));
6002aedd662SScott Long 	sc->copper_queue->dmatag = dmatag;
6012aedd662SScott Long 	sc->copper_queue->dmamap = dmamap;
6022aedd662SScott Long 	sc->copper_queue->nextstatus = 1;
6032aedd662SScott Long 	bus_dmamap_load(dmatag, dmamap,
6042aedd662SScott Long 			&(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4,
6052aedd662SScott Long 			ips_copperhead_queue_callback, sc->copper_queue,
6062aedd662SScott Long 			BUS_DMA_NOWAIT);
6072aedd662SScott Long 	if(sc->copper_queue->base_phys_addr == 0){
6082aedd662SScott Long 		error = ENOMEM;
6092aedd662SScott Long 		goto exit;
6102aedd662SScott Long 	}
6112aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr);
6122aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr +
6132aedd662SScott Long 		    IPS_MAX_CMD_NUM * 4);
6142aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4);
6152aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr);
6162aedd662SScott Long 
6172aedd662SScott Long 
6182aedd662SScott Long 	return 0;
6192aedd662SScott Long exit:
62076fe16bbSHiren Panchasara 	if (sc->copper_queue != NULL)
6212aedd662SScott Long 		bus_dmamem_free(dmatag, sc->copper_queue, dmamap);
6222aedd662SScott Long 	bus_dma_tag_destroy(dmatag);
6232aedd662SScott Long 	return error;
6242aedd662SScott Long }
6252aedd662SScott Long 
6262aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */
ips_copperhead_reinit(ips_softc_t * sc,int force)6272aedd662SScott Long int ips_copperhead_reinit(ips_softc_t *sc, int force)
6282aedd662SScott Long {
6292aedd662SScott Long 	int i, j;
630dec703c7SScott Long 	u_int32_t configstatus = 0;
6312aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, 0x80);
6322aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, 0);
6332aedd662SScott Long 	device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n");
6342aedd662SScott Long 	for(j = 0; j < 2; j++){
6352aedd662SScott Long 		for(i = 0; i < 45; i++){
6362aedd662SScott Long 			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
6372aedd662SScott Long 				ips_write_1(sc, COPPER_REG_HISR,
6382aedd662SScott Long 					    COPPER_GHI_BIT);
6392aedd662SScott Long 				break;
6402aedd662SScott Long 			} else
6412aedd662SScott Long 				DELAY(1000000);
6422aedd662SScott Long 		}
6432aedd662SScott Long 		if(i == 45)
6442aedd662SScott Long 			return 1;
6452aedd662SScott Long 	}
6462aedd662SScott Long 	for(j = 0; j < 2; j++){
6472aedd662SScott Long 		configstatus <<= 8;
6482aedd662SScott Long 		for(i = 0; i < 240; i++){
6492aedd662SScott Long 			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
6502aedd662SScott Long 				configstatus |= ips_read_1(sc, COPPER_REG_ISPR);
6512aedd662SScott Long 				ips_write_1(sc, COPPER_REG_HISR,
6522aedd662SScott Long 					    COPPER_GHI_BIT);
6532aedd662SScott Long 				break;
6542aedd662SScott Long 			} else
6552aedd662SScott Long 				DELAY(1000000);
6562aedd662SScott Long 		}
6572aedd662SScott Long 		if(i == 240)
6582aedd662SScott Long 			return 1;
6592aedd662SScott Long 	}
6602aedd662SScott Long 	for(i = 0; i < 240; i++){
6612aedd662SScott Long 		if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){
6622aedd662SScott Long 			break;
6632aedd662SScott Long 		} else
6642aedd662SScott Long 			DELAY(1000000);
6652aedd662SScott Long 	}
6662aedd662SScott Long 	if(i == 240)
6672aedd662SScott Long 		return 1;
6682aedd662SScott Long 	ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT);
6692aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT);
6702aedd662SScott Long 	ips_copperhead_queue_init(sc);
6712aedd662SScott Long 	ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT);
6722aedd662SScott Long 	i = ips_read_1(sc, COPPER_REG_SCPR);
6732aedd662SScott Long 	ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT);
6742aedd662SScott Long 	if(!configstatus){
6752aedd662SScott Long 		device_printf(sc->dev, "adapter initialization failed\n");
6762aedd662SScott Long 		return 1;
6772aedd662SScott Long 	}
6782aedd662SScott Long 	if(force && ips_clear_adapter(sc)){
6792aedd662SScott Long 		device_printf(sc->dev, "adapter clear failed\n");
6802aedd662SScott Long 		return 1;
6812aedd662SScott Long 	}
6822aedd662SScott Long 	return 0;
6832aedd662SScott Long }
ips_copperhead_cmd_status(ips_softc_t * sc)6842aedd662SScott Long static u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc)
6852aedd662SScott Long {
6862aedd662SScott Long 	u_int32_t value;
6872aedd662SScott Long 	int statnum = sc->copper_queue->nextstatus++;
6882aedd662SScott Long 	if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM)
6892aedd662SScott Long 		sc->copper_queue->nextstatus = 0;
6902aedd662SScott Long 	value = sc->copper_queue->status[statnum];
6912aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr +
6922aedd662SScott Long 		    4 * statnum);
6932aedd662SScott Long 	return value;
6942aedd662SScott Long }
6952aedd662SScott Long 
6962aedd662SScott Long 
ips_copperhead_intr(void * void_sc)6972aedd662SScott Long void ips_copperhead_intr(void *void_sc)
6982aedd662SScott Long {
6992aedd662SScott Long         ips_softc_t *sc = (ips_softc_t *)void_sc;
7002aedd662SScott Long 	int cmdnumber;
7012aedd662SScott Long 	ips_cmd_status_t status;
7022aedd662SScott Long 
70303a908f2SScott Long 	mtx_lock(&sc->queue_mtx);
7042aedd662SScott Long 	while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){
7052aedd662SScott Long 		status.value = ips_copperhead_cmd_status(sc);
7062aedd662SScott Long 		cmdnumber = status.fields.command_id;
7072aedd662SScott Long 		sc->commandarray[cmdnumber].status.value = status.value;
7082aedd662SScott Long 		sc->commandarray[cmdnumber].timeout = 0;
7092aedd662SScott Long 		sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
7102aedd662SScott Long 		PRINTF(9, "ips: got command %d\n", cmdnumber);
7112aedd662SScott Long 	}
71203a908f2SScott Long 	mtx_unlock(&sc->queue_mtx);
7132aedd662SScott Long         return;
7142aedd662SScott Long }
7152aedd662SScott Long 
ips_issue_copperhead_cmd(ips_command_t * command)7162aedd662SScott Long void ips_issue_copperhead_cmd(ips_command_t *command)
7172aedd662SScott Long {
7182aedd662SScott Long 	int i;
7192aedd662SScott Long 	/* hmmm, is there a cleaner way to do this? */
7202aedd662SScott Long 	if(command->sc->state & IPS_OFFLINE){
7212eea7051SScott Long 		ips_set_error(command, EINVAL);
7222aedd662SScott Long 		command->callback(command);
7232aedd662SScott Long 		return;
7242aedd662SScott Long 	}
7252aedd662SScott Long 	command->timeout = 10;
7262aedd662SScott Long 	for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT;
7272aedd662SScott Long 	    i++ ){
7282aedd662SScott Long 		if( i == 20){
7292aedd662SScott Long printf("sem bit still set, can't send a command\n");
7302aedd662SScott Long 			return;
7312aedd662SScott Long 		}
7322aedd662SScott Long 		DELAY(500);/* need to do a delay here */
7332aedd662SScott Long 	}
7342aedd662SScott Long 	ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr);
7352aedd662SScott Long 	ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START);
7362aedd662SScott Long }
7372aedd662SScott Long 
ips_copperhead_poll(ips_command_t * command)7387765040eSScott Long void ips_copperhead_poll(ips_command_t *command)
7397765040eSScott Long {
7407765040eSScott Long 
7417765040eSScott Long 	printf("ips: cmd polling not implemented for copperhead devices\n");
7427765040eSScott Long }
743