xref: /freebsd/sys/dev/ips/ips.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
12aedd662SScott Long /*-
2*4d846d26SWarner 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 
30aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
31aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
322aedd662SScott Long 
33e2e050c8SConrad Meyer #include <sys/types.h>
34e2e050c8SConrad Meyer #include <sys/lock.h>
35e2e050c8SConrad Meyer #include <sys/mutex.h>
362aedd662SScott Long #include <sys/stat.h>
372aedd662SScott Long #include <sys/time.h>
382aedd662SScott Long 
39e2e050c8SConrad Meyer #include <dev/ips/ipsreg.h>
40e2e050c8SConrad Meyer #include <dev/ips/ips.h>
41e2e050c8SConrad Meyer 
422aedd662SScott Long static d_open_t ips_open;
432aedd662SScott Long static d_close_t ips_close;
442aedd662SScott Long static d_ioctl_t ips_ioctl;
452aedd662SScott Long 
46dd83a01eSScott Long MALLOC_DEFINE(M_IPSBUF, "ipsbuf","IPS driver buffer");
47dd83a01eSScott Long 
482aedd662SScott Long static struct cdevsw ips_cdevsw = {
49dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
502aedd662SScott Long 	.d_open =	ips_open,
512aedd662SScott Long 	.d_close =	ips_close,
522aedd662SScott Long 	.d_ioctl =	ips_ioctl,
532aedd662SScott Long 	.d_name =	"ips",
542aedd662SScott Long };
552aedd662SScott Long 
567633e7f1SMartin Blapp static const char* ips_adapter_name[] = {
577633e7f1SMartin Blapp 	"N/A",
587633e7f1SMartin Blapp 	"ServeRAID (copperhead)",
597633e7f1SMartin Blapp 	"ServeRAID II (copperhead refresh)",
607633e7f1SMartin Blapp 	"ServeRAID onboard (copperhead)",
617633e7f1SMartin Blapp 	"ServeRAID onboard (copperhead)",
627633e7f1SMartin Blapp 	"ServeRAID 3H (clarinet)",
637633e7f1SMartin Blapp 	"ServeRAID 3L (clarinet lite)",
647633e7f1SMartin Blapp 	"ServeRAID 4H (trombone)",
657633e7f1SMartin Blapp 	"ServeRAID 4M (morpheus)",
667633e7f1SMartin Blapp 	"ServeRAID 4L (morpheus lite)",
677633e7f1SMartin Blapp 	"ServeRAID 4Mx (neo)",
687633e7f1SMartin Blapp 	"ServeRAID 4Lx (neo lite)",
697633e7f1SMartin Blapp 	"ServeRAID 5i II (sarasota)",
707633e7f1SMartin Blapp 	"ServeRAID 5i (sarasota)",
717633e7f1SMartin Blapp 	"ServeRAID 6M (marco)",
72bd4fb874SMaxim Konovalov 	"ServeRAID 6i (sebring)",
73bd4fb874SMaxim Konovalov 	"ServeRAID 7t",
74bd4fb874SMaxim Konovalov 	"ServeRAID 7k",
75bd4fb874SMaxim Konovalov 	"ServeRAID 7M"
767633e7f1SMartin Blapp };
777633e7f1SMartin Blapp 
782aedd662SScott Long 
7989c9c53dSPoul-Henning Kamp static int ips_open(struct cdev *dev, int flags, int fmt, struct thread *td)
802aedd662SScott Long {
812aedd662SScott Long 	ips_softc_t *sc = dev->si_drv1;
82352176c8SJohn Baldwin 	mtx_lock(&sc->queue_mtx);
832aedd662SScott Long 	sc->state |= IPS_DEV_OPEN;
84352176c8SJohn Baldwin 	mtx_unlock(&sc->queue_mtx);
852aedd662SScott Long         return 0;
862aedd662SScott Long }
872aedd662SScott Long 
8889c9c53dSPoul-Henning Kamp static int ips_close(struct cdev *dev, int flags, int fmt, struct thread *td)
892aedd662SScott Long {
902aedd662SScott Long 	ips_softc_t *sc = dev->si_drv1;
91352176c8SJohn Baldwin 
92352176c8SJohn Baldwin 	mtx_lock(&sc->queue_mtx);
932aedd662SScott Long 	sc->state &= ~IPS_DEV_OPEN;
94352176c8SJohn Baldwin 	mtx_unlock(&sc->queue_mtx);
952aedd662SScott Long 
962aedd662SScott Long         return 0;
972aedd662SScott Long }
982aedd662SScott Long 
9989c9c53dSPoul-Henning Kamp static int ips_ioctl(struct cdev *dev, u_long command, caddr_t addr, int32_t flags, struct thread *td)
1002aedd662SScott Long {
1012aedd662SScott Long 	ips_softc_t *sc;
1022aedd662SScott Long 
1032aedd662SScott Long 	sc = dev->si_drv1;
1042aedd662SScott Long 	return ips_ioctl_request(sc, command, addr, flags);
1052aedd662SScott Long }
1062aedd662SScott Long 
1072aedd662SScott Long static void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
1082aedd662SScott Long {
1092aedd662SScott Long 	ips_command_t *command = cmdptr;
1102aedd662SScott Long 	PRINTF(10, "ips: in ips_cmd_dmaload\n");
1112aedd662SScott Long 	if(!error)
1122aedd662SScott Long 		command->command_phys_addr = segments[0].ds_addr;
1132aedd662SScott Long 
1142aedd662SScott Long }
1152aedd662SScott Long 
116453130d9SPedro F. Giffuni /* is locking needed? what locking guarantees are there on removal? */
11703a908f2SScott Long static int ips_cmdqueue_free(ips_softc_t *sc)
1182aedd662SScott Long {
1192aedd662SScott Long 	int i, error = -1;
120b234a120SScott Long 	ips_command_t *command;
121b234a120SScott Long 
1222aedd662SScott Long 	if(!sc->used_commands){
1232aedd662SScott Long 		for(i = 0; i < sc->max_cmds; i++){
124b234a120SScott Long 
125b234a120SScott Long 			command = &sc->commandarray[i];
126b234a120SScott Long 
127b234a120SScott Long 			if(command->command_phys_addr == 0)
1282aedd662SScott Long 				continue;
1292aedd662SScott Long 			bus_dmamap_unload(sc->command_dmatag,
130b234a120SScott Long 					  command->command_dmamap);
1312aedd662SScott Long 			bus_dmamem_free(sc->command_dmatag,
132b234a120SScott Long 					command->command_buffer,
133b234a120SScott Long 					command->command_dmamap);
13403a908f2SScott Long 			if (command->data_dmamap != NULL)
13503a908f2SScott Long 				bus_dmamap_destroy(command->data_dmatag,
13603a908f2SScott Long 				    command->data_dmamap);
1372aedd662SScott Long 		}
1382aedd662SScott Long 		error = 0;
1392aedd662SScott Long 		sc->state |= IPS_OFFLINE;
1402aedd662SScott Long 	}
14103a908f2SScott Long 	sc->staticcmd = NULL;
14203a908f2SScott Long 	free(sc->commandarray, M_DEVBUF);
1432aedd662SScott Long 	return error;
1442aedd662SScott Long }
1452aedd662SScott Long 
1462aedd662SScott Long /* places all ips command structs on the free command queue.  No locking as if someone else tries
1472aedd662SScott Long  * to access this during init, we have bigger problems */
14803a908f2SScott Long static int ips_cmdqueue_init(ips_softc_t *sc)
1492aedd662SScott Long {
1502aedd662SScott Long 	int i;
1512aedd662SScott Long 	ips_command_t *command;
15203a908f2SScott Long 
15303a908f2SScott Long 	sc->commandarray = (ips_command_t *)malloc(sizeof(ips_command_t) *
15403a908f2SScott Long 	    sc->max_cmds, M_DEVBUF, M_NOWAIT|M_ZERO);
15503a908f2SScott Long 	if (sc->commandarray == NULL)
15603a908f2SScott Long 		return (ENOMEM);
15703a908f2SScott Long 
1582aedd662SScott Long 	SLIST_INIT(&sc->free_cmd_list);
1592aedd662SScott Long 	for(i = 0; i < sc->max_cmds; i++){
1602aedd662SScott Long 		command = &sc->commandarray[i];
161b234a120SScott Long 		command->id = i;
162b234a120SScott Long 		command->sc = sc;
163b234a120SScott Long 
1642aedd662SScott Long 		if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer,
1652aedd662SScott Long 		    BUS_DMA_NOWAIT, &command->command_dmamap))
1662aedd662SScott Long 			goto error;
1672aedd662SScott Long 		bus_dmamap_load(sc->command_dmatag, command->command_dmamap,
1682aedd662SScott Long 				command->command_buffer,IPS_COMMAND_LEN,
1692aedd662SScott Long 				ips_cmd_dmaload, command, BUS_DMA_NOWAIT);
1702aedd662SScott Long 		if(!command->command_phys_addr){
1712aedd662SScott Long 			bus_dmamem_free(sc->command_dmatag,
1722aedd662SScott Long 			    command->command_buffer, command->command_dmamap);
1732aedd662SScott Long 			goto error;
1742aedd662SScott Long 		}
175b234a120SScott Long 
17603a908f2SScott Long 		if (i != 0) {
17703a908f2SScott Long 			command->data_dmatag = sc->sg_dmatag;
17803a908f2SScott Long 			if (bus_dmamap_create(command->data_dmatag, 0,
17903a908f2SScott Long 			    &command->data_dmamap))
18003a908f2SScott Long 				goto error;
181b234a120SScott Long 			SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
18203a908f2SScott Long 		} else
18303a908f2SScott Long 			sc->staticcmd = command;
1842aedd662SScott Long 	}
1852aedd662SScott Long 	sc->state &= ~IPS_OFFLINE;
1862aedd662SScott Long 	return 0;
1872aedd662SScott Long error:
1882aedd662SScott Long 	ips_cmdqueue_free(sc);
1892aedd662SScott Long 	return ENOMEM;
1902aedd662SScott Long }
1912aedd662SScott Long 
1922aedd662SScott Long /* returns a free command struct if one is available.
1932aedd662SScott Long  * It also blanks out anything that may be a wild pointer/value.
1942aedd662SScott Long  * Also, command buffers are not freed.  They are
1952aedd662SScott Long  * small so they are saved and kept dmamapped and loaded.
1962aedd662SScott Long  */
19703a908f2SScott Long int ips_get_free_cmd(ips_softc_t *sc, ips_command_t **cmd, unsigned long flags)
1982aedd662SScott Long {
1992aedd662SScott Long 	ips_command_t *command;
2002aedd662SScott Long 
2012aedd662SScott Long 	if(sc->state & IPS_OFFLINE){
2022aedd662SScott Long 		return EIO;
2032aedd662SScott Long 	}
20403a908f2SScott Long 	if ((flags & IPS_STATIC_FLAG) == 0) {
2052aedd662SScott Long 		command = SLIST_FIRST(&sc->free_cmd_list);
2062aedd662SScott Long 		if(!command || (sc->state & IPS_TIMEOUT)){
20703a908f2SScott Long 			return EBUSY;
2082aedd662SScott Long 		}
2092aedd662SScott Long 		SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
2102aedd662SScott Long 		(sc->used_commands)++;
21103a908f2SScott Long 	} else {
21203a908f2SScott Long 		if (sc->state & IPS_STATIC_BUSY)
21303a908f2SScott Long 			return EAGAIN;
21403a908f2SScott Long 		command = sc->staticcmd;
21503a908f2SScott Long 		sc->state |= IPS_STATIC_BUSY;
21603a908f2SScott Long 	}
2172aedd662SScott Long 	clear_ips_command(command);
2182aedd662SScott Long 	bzero(command->command_buffer, IPS_COMMAND_LEN);
21903a908f2SScott Long 	*cmd = command;
22003a908f2SScott Long 	return 0;
2212aedd662SScott Long }
2222aedd662SScott Long 
2232aedd662SScott Long /* adds a command back to the free command queue */
2242aedd662SScott Long void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command)
2252aedd662SScott Long {
226b234a120SScott Long 
22703a908f2SScott Long 	if (sema_value(&sc->cmd_sema) != 0)
228b234a120SScott Long 		panic("ips: command returned non-zero semaphore");
229b234a120SScott Long 
23003a908f2SScott Long 	if (command != sc->staticcmd) {
2312aedd662SScott Long 		SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
2322aedd662SScott Long 		(sc->used_commands)--;
23303a908f2SScott Long 	} else {
23403a908f2SScott Long 		sc->state &= ~IPS_STATIC_BUSY;
23503a908f2SScott Long 	}
2362aedd662SScott Long }
2377633e7f1SMartin Blapp static const char* ips_diskdev_statename(u_int8_t state)
2387633e7f1SMartin Blapp {
2397633e7f1SMartin Blapp 	static char statebuf[20];
2407633e7f1SMartin Blapp 	switch(state){
2417633e7f1SMartin Blapp 		case IPS_LD_OFFLINE:
2427633e7f1SMartin Blapp 			return("OFFLINE");
2437633e7f1SMartin Blapp 			break;
2447633e7f1SMartin Blapp 		case IPS_LD_OKAY:
2457633e7f1SMartin Blapp 			return("OK");
2467633e7f1SMartin Blapp 			break;
2477633e7f1SMartin Blapp 		case IPS_LD_DEGRADED:
2487633e7f1SMartin Blapp 			return("DEGRADED");
2497633e7f1SMartin Blapp 			break;
2507633e7f1SMartin Blapp 		case IPS_LD_FREE:
2517633e7f1SMartin Blapp 			return("FREE");
2527633e7f1SMartin Blapp 			break;
2537633e7f1SMartin Blapp 		case IPS_LD_SYS:
2547633e7f1SMartin Blapp 			return("SYS");
2557633e7f1SMartin Blapp 			break;
2567633e7f1SMartin Blapp 		case IPS_LD_CRS:
2577633e7f1SMartin Blapp 			return("CRS");
2587633e7f1SMartin Blapp 			break;
2597633e7f1SMartin Blapp 	}
2607633e7f1SMartin Blapp 	sprintf(statebuf,"UNKNOWN(0x%02x)", state);
2617633e7f1SMartin Blapp 	return(statebuf);
2627633e7f1SMartin Blapp }
2632aedd662SScott Long 
2642aedd662SScott Long static int ips_diskdev_init(ips_softc_t *sc)
2652aedd662SScott Long {
2662aedd662SScott Long 	int i;
2672aedd662SScott Long 	for(i=0; i < IPS_MAX_NUM_DRIVES; i++){
2687633e7f1SMartin Blapp 		if(sc->drives[i].state == IPS_LD_FREE) continue;
2697633e7f1SMartin Blapp 		device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n",
2707633e7f1SMartin Blapp 			i, sc->drives[i].raid_lvl,
2717633e7f1SMartin Blapp 			sc->drives[i].sector_count,
2727633e7f1SMartin Blapp 			ips_diskdev_statename(sc->drives[i].state));
2737633e7f1SMartin Blapp 		if(sc->drives[i].state == IPS_LD_OKAY ||
2747633e7f1SMartin Blapp 		   sc->drives[i].state == IPS_LD_DEGRADED){
2752aedd662SScott Long 			sc->diskdev[i] = device_add_child(sc->dev, NULL, -1);
276f472527cSPeter Wemm 			device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i);
2772aedd662SScott Long 		}
2782aedd662SScott Long 	}
2792aedd662SScott Long 	if(bus_generic_attach(sc->dev)){
2802aedd662SScott Long 		device_printf(sc->dev, "Attaching bus failed\n");
2812aedd662SScott Long 	}
2822aedd662SScott Long 	return 0;
2832aedd662SScott Long }
2842aedd662SScott Long 
2852aedd662SScott Long static int ips_diskdev_free(ips_softc_t *sc)
2862aedd662SScott Long {
2872aedd662SScott Long 	int i;
2882aedd662SScott Long 	int error = 0;
2892aedd662SScott Long 	for(i = 0; i < IPS_MAX_NUM_DRIVES; i++){
2906c9e56b2SLi-Wen Hsu 		if(sc->diskdev[i]) {
2912aedd662SScott Long 			error = device_delete_child(sc->dev, sc->diskdev[i]);
2922aedd662SScott Long 			if(error)
2932aedd662SScott Long 				return error;
2942aedd662SScott Long 		}
2956c9e56b2SLi-Wen Hsu 	}
2962aedd662SScott Long 	bus_generic_detach(sc->dev);
2972aedd662SScott Long 	return 0;
2982aedd662SScott Long }
2992aedd662SScott Long 
3002aedd662SScott Long /* ips_timeout is periodically called to make sure no commands sent
3012aedd662SScott Long  * to the card have become stuck.  If it finds a stuck command, it
3022aedd662SScott Long  * sets a flag so the driver won't start any more commands and then
3032aedd662SScott Long  * is periodically called to see if all outstanding commands have
3042aedd662SScott Long  * either finished or timed out.  Once timed out, an attempt to
3052aedd662SScott Long  * reinitialize the card is made.  If that fails, the driver gives
3062aedd662SScott Long  * up and declares the card dead. */
3072aedd662SScott Long static void ips_timeout(void *arg)
3082aedd662SScott Long {
3092aedd662SScott Long 	ips_softc_t *sc = arg;
3102aedd662SScott Long 	int i, state = 0;
3112aedd662SScott Long 	ips_command_t *command;
31203a908f2SScott Long 
313352176c8SJohn Baldwin 	mtx_assert(&sc->queue_mtx, MA_OWNED);
3142aedd662SScott Long 	command = &sc->commandarray[0];
3152aedd662SScott Long 	for(i = 0; i < sc->max_cmds; i++){
3162aedd662SScott Long 		if(!command[i].timeout){
3172aedd662SScott Long 			continue;
3182aedd662SScott Long 		}
3192aedd662SScott Long 		command[i].timeout--;
3202aedd662SScott Long 		if(!command[i].timeout){
3212aedd662SScott Long 			if(!(sc->state & IPS_TIMEOUT)){
3222aedd662SScott Long 				sc->state |= IPS_TIMEOUT;
3232aedd662SScott Long 				device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n");
3242aedd662SScott Long 			}
3252eea7051SScott Long 			ips_set_error(&command[i], ETIMEDOUT);
3262aedd662SScott Long 			command[i].callback(&command[i]);
3272aedd662SScott Long 			/* hmm, this should be enough cleanup */
3282aedd662SScott Long 		} else
3292aedd662SScott Long 			state = 1;
3302aedd662SScott Long 	}
3312aedd662SScott Long 	if(!state && (sc->state & IPS_TIMEOUT)){
3322aedd662SScott Long 		if(sc->ips_adapter_reinit(sc, 1)){
3332aedd662SScott Long 			device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n");
3342aedd662SScott Long 			sc->state |= IPS_OFFLINE;
3352aedd662SScott Long 			sc->state &= ~IPS_TIMEOUT;
3362aedd662SScott Long 			/* Grr, I hate this solution. I run waiting commands
3372aedd662SScott Long 			   one at a time and error them out just before they
3382aedd662SScott Long 			   would go to the card. This sucks. */
3392aedd662SScott Long 		} else
3402aedd662SScott Long 			sc->state &= ~IPS_TIMEOUT;
3412aedd662SScott Long 	}
3422aedd662SScott Long 	if (sc->state != IPS_OFFLINE)
343352176c8SJohn Baldwin 		callout_reset(&sc->timer, 10 * hz, ips_timeout, sc);
3442aedd662SScott Long }
3452aedd662SScott Long 
3462aedd662SScott Long /* check card and initialize it */
3472aedd662SScott Long int ips_adapter_init(ips_softc_t *sc)
3482aedd662SScott Long {
349dea4622dSScott Long         int i;
3502aedd662SScott Long         DEVICE_PRINTF(1,sc->dev, "initializing\n");
351b234a120SScott Long 
3522aedd662SScott Long         if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
3532aedd662SScott Long 				/* alignemnt */	1,
3542aedd662SScott Long 				/* boundary  */	0,
3552aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
3562aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
3572aedd662SScott Long 				/* filter    */	NULL,
3582aedd662SScott Long 				/* filterarg */	NULL,
3592aedd662SScott Long 				/* maxsize   */	IPS_COMMAND_LEN +
3602aedd662SScott Long 						    IPS_MAX_SG_LEN,
3612aedd662SScott Long 				/* numsegs   */	1,
3622aedd662SScott Long 				/* maxsegsize*/	IPS_COMMAND_LEN +
3632aedd662SScott Long 						    IPS_MAX_SG_LEN,
3642aedd662SScott Long 				/* flags     */	0,
36503a908f2SScott Long 				/* lockfunc  */ NULL,
36603a908f2SScott Long 				/* lockarg   */ NULL,
3672aedd662SScott Long 				&sc->command_dmatag) != 0) {
3682aedd662SScott Long                 device_printf(sc->dev, "can't alloc command dma tag\n");
3692aedd662SScott Long 		goto error;
3702aedd662SScott Long         }
3712aedd662SScott Long 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
3722aedd662SScott Long 				/* alignemnt */	1,
3732aedd662SScott Long 				/* boundary  */	0,
3742aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
3752aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
3762aedd662SScott Long 				/* filter    */	NULL,
3772aedd662SScott Long 				/* filterarg */	NULL,
3782aedd662SScott Long 				/* maxsize   */	IPS_MAX_IOBUF_SIZE,
3792aedd662SScott Long 				/* numsegs   */	IPS_MAX_SG_ELEMENTS,
3802aedd662SScott Long 				/* maxsegsize*/	IPS_MAX_IOBUF_SIZE,
3812aedd662SScott Long 				/* flags     */	0,
382f6b1c44dSScott Long 				/* lockfunc  */ busdma_lock_mutex,
38303a908f2SScott Long 				/* lockarg   */ &sc->queue_mtx,
3842aedd662SScott Long 				&sc->sg_dmatag) != 0) {
3852aedd662SScott Long 		device_printf(sc->dev, "can't alloc SG dma tag\n");
3862aedd662SScott Long 		goto error;
3872aedd662SScott Long 	}
3882aedd662SScott Long 	/* create one command buffer until we know how many commands this card
3892aedd662SScott Long            can handle */
3902aedd662SScott Long 	sc->max_cmds = 1;
3912aedd662SScott Long 	ips_cmdqueue_init(sc);
3922aedd662SScott Long 
3932aedd662SScott Long 	if(sc->ips_adapter_reinit(sc, 0))
3942aedd662SScott Long 		goto error;
3952aedd662SScott Long 
3967633e7f1SMartin Blapp 	/* initialize ffdc values */
3977633e7f1SMartin Blapp 	microtime(&sc->ffdc_resettime);
3987633e7f1SMartin Blapp 	sc->ffdc_resetcount = 1;
3997633e7f1SMartin Blapp 	if ((i = ips_ffdc_reset(sc)) != 0) {
4007633e7f1SMartin Blapp 		device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i);
4017633e7f1SMartin Blapp 		goto error;
4027633e7f1SMartin Blapp 	}
403dea4622dSScott Long 	if ((i = ips_get_adapter_info(sc)) != 0) {
404dea4622dSScott Long 		device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i);
405dea4622dSScott Long 		goto error;
406dea4622dSScott Long 	}
4077633e7f1SMartin Blapp 	ips_update_nvram(sc); /* no error check as failure doesn't matter */
4087633e7f1SMartin Blapp 	if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){
4097633e7f1SMartin Blapp 		device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]);
4107633e7f1SMartin Blapp 	}
411dea4622dSScott Long  	if ((i = ips_get_drive_info(sc)) != 0) {
412dea4622dSScott Long 		device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i);
4132aedd662SScott Long 		goto error;
4142aedd662SScott Long 	}
4152aedd662SScott Long 
4162aedd662SScott Long         ips_cmdqueue_free(sc);
4172aedd662SScott Long 	if(sc->adapter_info.max_concurrent_cmds)
4182aedd662SScott Long         	sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds);
4192aedd662SScott Long 	else
4202aedd662SScott Long 		sc->max_cmds = 32;
4212aedd662SScott Long         if(ips_cmdqueue_init(sc)){
4222aedd662SScott Long 		device_printf(sc->dev, "failed to initialize command buffers\n");
4232aedd662SScott Long 		goto error;
4242aedd662SScott Long 	}
4252aedd662SScott Long         sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR,
4262aedd662SScott Long                                         S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev));
4272aedd662SScott Long 	sc->device_file->si_drv1 = sc;
4282aedd662SScott Long 	ips_diskdev_init(sc);
429352176c8SJohn Baldwin 	callout_reset(&sc->timer, 10 * hz, ips_timeout, sc);
4302aedd662SScott Long         return 0;
4312aedd662SScott Long 
4322aedd662SScott Long error:
4332aedd662SScott Long 	ips_adapter_free(sc);
4342aedd662SScott Long 	return ENXIO;
4352aedd662SScott Long }
4362aedd662SScott Long 
4372aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization */
4382aedd662SScott Long int ips_morpheus_reinit(ips_softc_t *sc, int force)
4392aedd662SScott Long {
4402aedd662SScott Long         u_int32_t tmp;
4412aedd662SScott Long 	int i;
4422aedd662SScott Long 
4432aedd662SScott Long 	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4442aedd662SScott Long 	if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) &&
4452aedd662SScott Long 	    (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){
4462aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
4472aedd662SScott Long 		return 0;
4482aedd662SScott Long 	}
4492aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff);
4502aedd662SScott Long 	ips_read_4(sc, MORPHEUS_REG_OIMR);
4512aedd662SScott Long 
4522aedd662SScott Long 	device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n");
4532aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000);
4542aedd662SScott Long 	DELAY(5000000);
455f94dfeb4SScott Long 	ips_read_4(sc, MORPHEUS_REG_OIMR);
4562aedd662SScott Long 
4572aedd662SScott Long 	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4582aedd662SScott Long 	for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){
4592aedd662SScott Long 		DELAY(1000000);
4602aedd662SScott Long 		DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i);
4612aedd662SScott Long 		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4622aedd662SScott Long 	}
4632aedd662SScott Long 	if(tmp & MORPHEUS_BIT_POST1)
4642aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1);
4652aedd662SScott Long 
4662aedd662SScott Long         if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){
4672aedd662SScott Long                 device_printf(sc->dev,"Adapter error during initialization.\n");
4682aedd662SScott Long 		return 1;
4692aedd662SScott Long         }
4702aedd662SScott Long 	for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){
4712aedd662SScott Long 		DELAY(1000000);
4722aedd662SScott Long 		DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i);
4732aedd662SScott Long 		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4742aedd662SScott Long 	}
4752aedd662SScott Long 	if(tmp & MORPHEUS_BIT_POST2)
4762aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2);
4772aedd662SScott Long 
4782aedd662SScott Long 	if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){
4792aedd662SScott Long 		device_printf(sc->dev, "adapter failed config check\n");
4802aedd662SScott Long 		return 1;
4812aedd662SScott Long         }
4822aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
4832aedd662SScott Long 	if(force && ips_clear_adapter(sc)){
4842aedd662SScott Long 		device_printf(sc->dev, "adapter clear failed\n");
4852aedd662SScott Long 		return 1;
4862aedd662SScott Long 	}
4872aedd662SScott Long 	return 0;
4882aedd662SScott Long }
4892aedd662SScott Long 
4902aedd662SScott Long /* clean up so we can unload the driver. */
4912aedd662SScott Long int ips_adapter_free(ips_softc_t *sc)
4922aedd662SScott Long {
4932aedd662SScott Long 	int error = 0;
4942aedd662SScott Long 	if(sc->state & IPS_DEV_OPEN)
4952aedd662SScott Long 		return EBUSY;
4962aedd662SScott Long 	if((error = ips_diskdev_free(sc)))
4972aedd662SScott Long 		return error;
4982aedd662SScott Long 	if(ips_cmdqueue_free(sc)){
4992aedd662SScott Long 		device_printf(sc->dev,
5002aedd662SScott Long 		     "trying to exit when command queue is not empty!\n");
5012aedd662SScott Long 		return EBUSY;
5022aedd662SScott Long 	}
5032aedd662SScott Long 	DEVICE_PRINTF(1, sc->dev, "free\n");
504352176c8SJohn Baldwin 	callout_drain(&sc->timer);
5052aedd662SScott Long 
5062aedd662SScott Long 	if(sc->sg_dmatag)
5072aedd662SScott Long 		bus_dma_tag_destroy(sc->sg_dmatag);
5082aedd662SScott Long 	if(sc->command_dmatag)
5092aedd662SScott Long 		bus_dma_tag_destroy(sc->command_dmatag);
5102aedd662SScott Long 	if(sc->device_file)
5112aedd662SScott Long 	        destroy_dev(sc->device_file);
5122aedd662SScott Long         return 0;
5132aedd662SScott Long }
5142aedd662SScott Long 
5157765040eSScott Long static __inline int ips_morpheus_check_intr(ips_softc_t *sc)
5162aedd662SScott Long {
5172aedd662SScott Long 	int cmdnumber;
5182aedd662SScott Long 	ips_cmd_status_t status;
5197765040eSScott Long 	ips_command_t *command;
5207765040eSScott Long 	int found = 0;
5217765040eSScott Long 	u_int32_t oisr;
5222aedd662SScott Long 
5232aedd662SScott Long 	oisr = ips_read_4(sc, MORPHEUS_REG_OISR);
5247765040eSScott Long 	PRINTF(9, "interrupt registers out:%x\n", oisr);
5252aedd662SScott Long 	if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){
5262aedd662SScott Long 		DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n");
5277765040eSScott Long 		return (0);
5282aedd662SScott Long 	}
5292aedd662SScott Long 	while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){
5302aedd662SScott Long 		cmdnumber = status.fields.command_id;
5317765040eSScott Long 		command = &sc->commandarray[cmdnumber];
5327765040eSScott Long 		command->status.value = status.value;
5337765040eSScott Long 		command->timeout = 0;
5347765040eSScott Long 		command->callback(command);
5352aedd662SScott Long 
5367765040eSScott Long 		found = 1;
5372aedd662SScott Long 	}
5387765040eSScott Long         return (found);
5397765040eSScott Long }
5407765040eSScott Long 
5417765040eSScott Long void ips_morpheus_intr(void *void_sc)
5427765040eSScott Long {
5437765040eSScott Long 	ips_softc_t *sc = void_sc;
5447765040eSScott Long 
5457765040eSScott Long 	mtx_lock(&sc->queue_mtx);
5467765040eSScott Long 	ips_morpheus_check_intr(sc);
54703a908f2SScott Long 	mtx_unlock(&sc->queue_mtx);
5487765040eSScott Long }
5497765040eSScott Long 
5507765040eSScott Long void ips_morpheus_poll(ips_command_t *command)
5517765040eSScott Long {
5527765040eSScott Long 	uint32_t ts;
5537765040eSScott Long 
5544ef63badSScott Long 	/*
5554ef63badSScott Long 	 * Locks are not used here because this is only called during
5564ef63badSScott Long 	 * crashdumps.
5574ef63badSScott Long 	 */
5587765040eSScott Long 	ts = time_second + command->timeout;
5597765040eSScott Long 	while ((command->timeout != 0)
5607765040eSScott Long 	 && (ips_morpheus_check_intr(command->sc) == 0)
5617765040eSScott Long 	 && (ts > time_second))
5627765040eSScott Long 		DELAY(1000);
5632aedd662SScott Long }
5642aedd662SScott Long 
5652aedd662SScott Long void ips_issue_morpheus_cmd(ips_command_t *command)
5662aedd662SScott Long {
5672aedd662SScott Long 	/* hmmm, is there a cleaner way to do this? */
5682aedd662SScott Long 	if(command->sc->state & IPS_OFFLINE){
5692eea7051SScott Long 		ips_set_error(command, EINVAL);
5702aedd662SScott Long 		command->callback(command);
5712aedd662SScott Long 		return;
5722aedd662SScott Long 	}
5732aedd662SScott Long 	command->timeout = 10;
5742aedd662SScott Long 	ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr);
5752aedd662SScott Long }
5762aedd662SScott Long 
5772aedd662SScott Long static void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error)
5782aedd662SScott Long {
5792aedd662SScott Long 	ips_copper_queue_t *queue = queueptr;
5802aedd662SScott Long 	if(error){
5812aedd662SScott Long 		return;
5822aedd662SScott Long 	}
5832aedd662SScott Long 	queue->base_phys_addr = segments[0].ds_addr;
5842aedd662SScott Long }
5852aedd662SScott Long 
5862aedd662SScott Long static int ips_copperhead_queue_init(ips_softc_t *sc)
5872aedd662SScott Long {
5882aedd662SScott Long 	int error;
5892aedd662SScott Long 	bus_dma_tag_t dmatag;
59076fe16bbSHiren Panchasara 	bus_dmamap_t dmamap;
5912aedd662SScott Long        	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
5922aedd662SScott Long 				/* alignemnt */	1,
5932aedd662SScott Long 				/* boundary  */	0,
5942aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
5952aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
5962aedd662SScott Long 				/* filter    */	NULL,
5972aedd662SScott Long 				/* filterarg */	NULL,
5982aedd662SScott Long 				/* maxsize   */	sizeof(ips_copper_queue_t),
5992aedd662SScott Long 				/* numsegs   */	1,
6002aedd662SScott Long 				/* maxsegsize*/	sizeof(ips_copper_queue_t),
6012aedd662SScott Long 				/* flags     */	0,
60203a908f2SScott Long 				/* lockfunc  */ NULL,
60303a908f2SScott Long 				/* lockarg   */ NULL,
6042aedd662SScott Long 				&dmatag) != 0) {
6052aedd662SScott Long                 device_printf(sc->dev, "can't alloc dma tag for statue queue\n");
6062aedd662SScott Long 		error = ENOMEM;
60776fe16bbSHiren Panchasara 		return error;
6082aedd662SScott Long         }
6092aedd662SScott Long 	if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue),
6102aedd662SScott Long 	   		    BUS_DMA_NOWAIT, &dmamap)){
6112aedd662SScott Long 		error = ENOMEM;
6122aedd662SScott Long 		goto exit;
6132aedd662SScott Long 	}
6142aedd662SScott Long 	bzero(sc->copper_queue, sizeof(ips_copper_queue_t));
6152aedd662SScott Long 	sc->copper_queue->dmatag = dmatag;
6162aedd662SScott Long 	sc->copper_queue->dmamap = dmamap;
6172aedd662SScott Long 	sc->copper_queue->nextstatus = 1;
6182aedd662SScott Long 	bus_dmamap_load(dmatag, dmamap,
6192aedd662SScott Long 			&(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4,
6202aedd662SScott Long 			ips_copperhead_queue_callback, sc->copper_queue,
6212aedd662SScott Long 			BUS_DMA_NOWAIT);
6222aedd662SScott Long 	if(sc->copper_queue->base_phys_addr == 0){
6232aedd662SScott Long 		error = ENOMEM;
6242aedd662SScott Long 		goto exit;
6252aedd662SScott Long 	}
6262aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr);
6272aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr +
6282aedd662SScott Long 		    IPS_MAX_CMD_NUM * 4);
6292aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4);
6302aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr);
6312aedd662SScott Long 
6322aedd662SScott Long 
6332aedd662SScott Long 	return 0;
6342aedd662SScott Long exit:
63576fe16bbSHiren Panchasara 	if (sc->copper_queue != NULL)
6362aedd662SScott Long 		bus_dmamem_free(dmatag, sc->copper_queue, dmamap);
6372aedd662SScott Long 	bus_dma_tag_destroy(dmatag);
6382aedd662SScott Long 	return error;
6392aedd662SScott Long }
6402aedd662SScott Long 
6412aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */
6422aedd662SScott Long int ips_copperhead_reinit(ips_softc_t *sc, int force)
6432aedd662SScott Long {
6442aedd662SScott Long 	int i, j;
645dec703c7SScott Long 	u_int32_t configstatus = 0;
6462aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, 0x80);
6472aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, 0);
6482aedd662SScott Long 	device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n");
6492aedd662SScott Long 	for(j = 0; j < 2; j++){
6502aedd662SScott Long 		for(i = 0; i < 45; i++){
6512aedd662SScott Long 			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
6522aedd662SScott Long 				ips_write_1(sc, COPPER_REG_HISR,
6532aedd662SScott Long 					    COPPER_GHI_BIT);
6542aedd662SScott Long 				break;
6552aedd662SScott Long 			} else
6562aedd662SScott Long 				DELAY(1000000);
6572aedd662SScott Long 		}
6582aedd662SScott Long 		if(i == 45)
6592aedd662SScott Long 			return 1;
6602aedd662SScott Long 	}
6612aedd662SScott Long 	for(j = 0; j < 2; j++){
6622aedd662SScott Long 		configstatus <<= 8;
6632aedd662SScott Long 		for(i = 0; i < 240; i++){
6642aedd662SScott Long 			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
6652aedd662SScott Long 				configstatus |= ips_read_1(sc, COPPER_REG_ISPR);
6662aedd662SScott Long 				ips_write_1(sc, COPPER_REG_HISR,
6672aedd662SScott Long 					    COPPER_GHI_BIT);
6682aedd662SScott Long 				break;
6692aedd662SScott Long 			} else
6702aedd662SScott Long 				DELAY(1000000);
6712aedd662SScott Long 		}
6722aedd662SScott Long 		if(i == 240)
6732aedd662SScott Long 			return 1;
6742aedd662SScott Long 	}
6752aedd662SScott Long 	for(i = 0; i < 240; i++){
6762aedd662SScott Long 		if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){
6772aedd662SScott Long 			break;
6782aedd662SScott Long 		} else
6792aedd662SScott Long 			DELAY(1000000);
6802aedd662SScott Long 	}
6812aedd662SScott Long 	if(i == 240)
6822aedd662SScott Long 		return 1;
6832aedd662SScott Long 	ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT);
6842aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT);
6852aedd662SScott Long 	ips_copperhead_queue_init(sc);
6862aedd662SScott Long 	ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT);
6872aedd662SScott Long 	i = ips_read_1(sc, COPPER_REG_SCPR);
6882aedd662SScott Long 	ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT);
6892aedd662SScott Long 	if(!configstatus){
6902aedd662SScott Long 		device_printf(sc->dev, "adapter initialization failed\n");
6912aedd662SScott Long 		return 1;
6922aedd662SScott Long 	}
6932aedd662SScott Long 	if(force && ips_clear_adapter(sc)){
6942aedd662SScott Long 		device_printf(sc->dev, "adapter clear failed\n");
6952aedd662SScott Long 		return 1;
6962aedd662SScott Long 	}
6972aedd662SScott Long 	return 0;
6982aedd662SScott Long }
6992aedd662SScott Long static u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc)
7002aedd662SScott Long {
7012aedd662SScott Long 	u_int32_t value;
7022aedd662SScott Long 	int statnum = sc->copper_queue->nextstatus++;
7032aedd662SScott Long 	if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM)
7042aedd662SScott Long 		sc->copper_queue->nextstatus = 0;
7052aedd662SScott Long 	value = sc->copper_queue->status[statnum];
7062aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr +
7072aedd662SScott Long 		    4 * statnum);
7082aedd662SScott Long 	return value;
7092aedd662SScott Long }
7102aedd662SScott Long 
7112aedd662SScott Long 
7122aedd662SScott Long void ips_copperhead_intr(void *void_sc)
7132aedd662SScott Long {
7142aedd662SScott Long         ips_softc_t *sc = (ips_softc_t *)void_sc;
7152aedd662SScott Long 	int cmdnumber;
7162aedd662SScott Long 	ips_cmd_status_t status;
7172aedd662SScott Long 
71803a908f2SScott Long 	mtx_lock(&sc->queue_mtx);
7192aedd662SScott Long 	while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){
7202aedd662SScott Long 		status.value = ips_copperhead_cmd_status(sc);
7212aedd662SScott Long 		cmdnumber = status.fields.command_id;
7222aedd662SScott Long 		sc->commandarray[cmdnumber].status.value = status.value;
7232aedd662SScott Long 		sc->commandarray[cmdnumber].timeout = 0;
7242aedd662SScott Long 		sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
7252aedd662SScott Long 		PRINTF(9, "ips: got command %d\n", cmdnumber);
7262aedd662SScott Long 	}
72703a908f2SScott Long 	mtx_unlock(&sc->queue_mtx);
7282aedd662SScott Long         return;
7292aedd662SScott Long }
7302aedd662SScott Long 
7312aedd662SScott Long void ips_issue_copperhead_cmd(ips_command_t *command)
7322aedd662SScott Long {
7332aedd662SScott Long 	int i;
7342aedd662SScott Long 	/* hmmm, is there a cleaner way to do this? */
7352aedd662SScott Long 	if(command->sc->state & IPS_OFFLINE){
7362eea7051SScott Long 		ips_set_error(command, EINVAL);
7372aedd662SScott Long 		command->callback(command);
7382aedd662SScott Long 		return;
7392aedd662SScott Long 	}
7402aedd662SScott Long 	command->timeout = 10;
7412aedd662SScott Long 	for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT;
7422aedd662SScott Long 	    i++ ){
7432aedd662SScott Long 		if( i == 20){
7442aedd662SScott Long printf("sem bit still set, can't send a command\n");
7452aedd662SScott Long 			return;
7462aedd662SScott Long 		}
7472aedd662SScott Long 		DELAY(500);/* need to do a delay here */
7482aedd662SScott Long 	}
7492aedd662SScott Long 	ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr);
7502aedd662SScott Long 	ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START);
7512aedd662SScott Long }
7522aedd662SScott Long 
7537765040eSScott Long void ips_copperhead_poll(ips_command_t *command)
7547765040eSScott Long {
7557765040eSScott Long 
7567765040eSScott Long 	printf("ips: cmd polling not implemented for copperhead devices\n");
7577765040eSScott Long }
758