xref: /freebsd/sys/dev/ips/ips.c (revision f94dfeb4810245d485d85e8a494a3d339d7f1d95)
12aedd662SScott Long /*-
2aad970f1SDavid E. O'Brien  * Written by: David Jeffery
32aedd662SScott Long  * Copyright (c) 2002 Adaptec Inc.
42aedd662SScott Long  * All rights reserved.
52aedd662SScott Long  *
62aedd662SScott Long  * Redistribution and use in source and binary forms, with or without
72aedd662SScott Long  * modification, are permitted provided that the following conditions
82aedd662SScott Long  * are met:
92aedd662SScott Long  * 1. Redistributions of source code must retain the above copyright
102aedd662SScott Long  *    notice, this list of conditions and the following disclaimer.
112aedd662SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
122aedd662SScott Long  *    notice, this list of conditions and the following disclaimer in the
132aedd662SScott Long  *    documentation and/or other materials provided with the distribution.
142aedd662SScott Long  *
152aedd662SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
162aedd662SScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172aedd662SScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182aedd662SScott Long  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192aedd662SScott Long  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
202aedd662SScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
212aedd662SScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
222aedd662SScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
232aedd662SScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
242aedd662SScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
252aedd662SScott Long  * SUCH DAMAGE.
262aedd662SScott Long  */
272aedd662SScott Long 
28aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
29aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
302aedd662SScott Long 
31f94dfeb4SScott Long #include <dev/ips/ipsreg.h>
322aedd662SScott Long #include <dev/ips/ips.h>
332aedd662SScott Long #include <sys/stat.h>
342aedd662SScott Long #include <sys/time.h>
352aedd662SScott Long #include <machine/clock.h>
362aedd662SScott Long 
372aedd662SScott Long static d_open_t ips_open;
382aedd662SScott Long static d_close_t ips_close;
392aedd662SScott Long static d_ioctl_t ips_ioctl;
402aedd662SScott Long 
41dd83a01eSScott Long MALLOC_DEFINE(M_IPSBUF, "ipsbuf","IPS driver buffer");
42dd83a01eSScott Long 
432aedd662SScott Long static struct cdevsw ips_cdevsw = {
44dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
45dc08ffecSPoul-Henning Kamp 	.d_flags =	D_NEEDGIANT,
462aedd662SScott Long 	.d_open =	ips_open,
472aedd662SScott Long 	.d_close =	ips_close,
482aedd662SScott Long 	.d_ioctl =	ips_ioctl,
492aedd662SScott Long 	.d_name =	"ips",
502aedd662SScott Long };
512aedd662SScott Long 
527633e7f1SMartin Blapp static const char* ips_adapter_name[] = {
537633e7f1SMartin Blapp 	"N/A",
547633e7f1SMartin Blapp 	"ServeRAID (copperhead)",
557633e7f1SMartin Blapp 	"ServeRAID II (copperhead refresh)",
567633e7f1SMartin Blapp 	"ServeRAID onboard (copperhead)",
577633e7f1SMartin Blapp 	"ServeRAID onboard (copperhead)",
587633e7f1SMartin Blapp 	"ServeRAID 3H (clarinet)",
597633e7f1SMartin Blapp 	"ServeRAID 3L (clarinet lite)",
607633e7f1SMartin Blapp 	"ServeRAID 4H (trombone)",
617633e7f1SMartin Blapp 	"ServeRAID 4M (morpheus)",
627633e7f1SMartin Blapp 	"ServeRAID 4L (morpheus lite)",
637633e7f1SMartin Blapp 	"ServeRAID 4Mx (neo)",
647633e7f1SMartin Blapp 	"ServeRAID 4Lx (neo lite)",
657633e7f1SMartin Blapp 	"ServeRAID 5i II (sarasota)",
667633e7f1SMartin Blapp 	"ServeRAID 5i (sarasota)",
677633e7f1SMartin Blapp 	"ServeRAID 6M (marco)",
687633e7f1SMartin Blapp 	"ServeRAID 6i (sebring)"
697633e7f1SMartin Blapp };
707633e7f1SMartin Blapp 
712aedd662SScott Long 
7289c9c53dSPoul-Henning Kamp static int ips_open(struct cdev *dev, int flags, int fmt, struct thread *td)
732aedd662SScott Long {
742aedd662SScott Long 	ips_softc_t *sc = dev->si_drv1;
752aedd662SScott Long 	sc->state |= IPS_DEV_OPEN;
762aedd662SScott Long         return 0;
772aedd662SScott Long }
782aedd662SScott Long 
7989c9c53dSPoul-Henning Kamp static int ips_close(struct cdev *dev, int flags, int fmt, struct thread *td)
802aedd662SScott Long {
812aedd662SScott Long 	ips_softc_t *sc = dev->si_drv1;
822aedd662SScott Long 	sc->state &= ~IPS_DEV_OPEN;
832aedd662SScott Long 
842aedd662SScott Long         return 0;
852aedd662SScott Long }
862aedd662SScott Long 
8789c9c53dSPoul-Henning Kamp static int ips_ioctl(struct cdev *dev, u_long command, caddr_t addr, int32_t flags, struct thread *td)
882aedd662SScott Long {
892aedd662SScott Long 	ips_softc_t *sc;
902aedd662SScott Long 
912aedd662SScott Long 	sc = dev->si_drv1;
922aedd662SScott Long 	return ips_ioctl_request(sc, command, addr, flags);
932aedd662SScott Long }
942aedd662SScott Long 
952aedd662SScott Long static void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
962aedd662SScott Long {
972aedd662SScott Long 	ips_command_t *command = cmdptr;
982aedd662SScott Long 	PRINTF(10, "ips: in ips_cmd_dmaload\n");
992aedd662SScott Long 	if(!error)
1002aedd662SScott Long 		command->command_phys_addr = segments[0].ds_addr;
1012aedd662SScott Long 
1022aedd662SScott Long }
1032aedd662SScott Long 
1042aedd662SScott Long /* is locking needed? what locking guarentees are there on removal? */
10503a908f2SScott Long static int ips_cmdqueue_free(ips_softc_t *sc)
1062aedd662SScott Long {
1072aedd662SScott Long 	int i, error = -1;
108b234a120SScott Long 	ips_command_t *command;
109b234a120SScott Long 
1102aedd662SScott Long 	if(!sc->used_commands){
1112aedd662SScott Long 		for(i = 0; i < sc->max_cmds; i++){
112b234a120SScott Long 
113b234a120SScott Long 			command = &sc->commandarray[i];
114b234a120SScott Long 
115b234a120SScott Long 			if(command->command_phys_addr == 0)
1162aedd662SScott Long 				continue;
1172aedd662SScott Long 			bus_dmamap_unload(sc->command_dmatag,
118b234a120SScott Long 					  command->command_dmamap);
1192aedd662SScott Long 			bus_dmamem_free(sc->command_dmatag,
120b234a120SScott Long 					command->command_buffer,
121b234a120SScott Long 					command->command_dmamap);
12203a908f2SScott Long 			if (command->data_dmamap != NULL)
12303a908f2SScott Long 				bus_dmamap_destroy(command->data_dmatag,
12403a908f2SScott Long 				    command->data_dmamap);
1252aedd662SScott Long 		}
1262aedd662SScott Long 		error = 0;
1272aedd662SScott Long 		sc->state |= IPS_OFFLINE;
1282aedd662SScott Long 	}
12903a908f2SScott Long 	sc->staticcmd = NULL;
13003a908f2SScott Long 	free(sc->commandarray, M_DEVBUF);
1312aedd662SScott Long 	return error;
1322aedd662SScott Long }
1332aedd662SScott Long 
1342aedd662SScott Long /* places all ips command structs on the free command queue.  No locking as if someone else tries
1352aedd662SScott Long  * to access this during init, we have bigger problems */
13603a908f2SScott Long static int ips_cmdqueue_init(ips_softc_t *sc)
1372aedd662SScott Long {
1382aedd662SScott Long 	int i;
1392aedd662SScott Long 	ips_command_t *command;
14003a908f2SScott Long 
14103a908f2SScott Long 	sc->commandarray = (ips_command_t *)malloc(sizeof(ips_command_t) *
14203a908f2SScott Long 	    sc->max_cmds, M_DEVBUF, M_NOWAIT|M_ZERO);
14303a908f2SScott Long 	if (sc->commandarray == NULL)
14403a908f2SScott Long 		return (ENOMEM);
14503a908f2SScott Long 
1462aedd662SScott Long 	SLIST_INIT(&sc->free_cmd_list);
1472aedd662SScott Long 	for(i = 0; i < sc->max_cmds; i++){
1482aedd662SScott Long 		command = &sc->commandarray[i];
149b234a120SScott Long 		command->id = i;
150b234a120SScott Long 		command->sc = sc;
151b234a120SScott Long 
1522aedd662SScott Long 		if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer,
1532aedd662SScott Long 		    BUS_DMA_NOWAIT, &command->command_dmamap))
1542aedd662SScott Long 			goto error;
1552aedd662SScott Long 		bus_dmamap_load(sc->command_dmatag, command->command_dmamap,
1562aedd662SScott Long 				command->command_buffer,IPS_COMMAND_LEN,
1572aedd662SScott Long 				ips_cmd_dmaload, command, BUS_DMA_NOWAIT);
1582aedd662SScott Long 		if(!command->command_phys_addr){
1592aedd662SScott Long 			bus_dmamem_free(sc->command_dmatag,
1602aedd662SScott Long 			    command->command_buffer, command->command_dmamap);
1612aedd662SScott Long 			goto error;
1622aedd662SScott Long 		}
163b234a120SScott Long 
16403a908f2SScott Long 		if (i != 0) {
16503a908f2SScott Long 			command->data_dmatag = sc->sg_dmatag;
16603a908f2SScott Long 			if (bus_dmamap_create(command->data_dmatag, 0,
16703a908f2SScott Long 			    &command->data_dmamap))
16803a908f2SScott Long 				goto error;
169b234a120SScott Long 			SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
17003a908f2SScott Long 		} else
17103a908f2SScott Long 			sc->staticcmd = command;
1722aedd662SScott Long 	}
1732aedd662SScott Long 	sc->state &= ~IPS_OFFLINE;
1742aedd662SScott Long 	return 0;
1752aedd662SScott Long error:
1762aedd662SScott Long 	ips_cmdqueue_free(sc);
1772aedd662SScott Long 	return ENOMEM;
1782aedd662SScott Long }
1792aedd662SScott Long 
1802aedd662SScott Long /* returns a free command struct if one is available.
1812aedd662SScott Long  * It also blanks out anything that may be a wild pointer/value.
1822aedd662SScott Long  * Also, command buffers are not freed.  They are
1832aedd662SScott Long  * small so they are saved and kept dmamapped and loaded.
1842aedd662SScott Long  */
18503a908f2SScott Long int ips_get_free_cmd(ips_softc_t *sc, ips_command_t **cmd, unsigned long flags)
1862aedd662SScott Long {
1872aedd662SScott Long 	ips_command_t *command;
1882aedd662SScott Long 
1892aedd662SScott Long 	if(sc->state & IPS_OFFLINE){
1902aedd662SScott Long 		return EIO;
1912aedd662SScott Long 	}
19203a908f2SScott Long 	if ((flags & IPS_STATIC_FLAG) == 0) {
1932aedd662SScott Long 		command = SLIST_FIRST(&sc->free_cmd_list);
1942aedd662SScott Long 		if(!command || (sc->state & IPS_TIMEOUT)){
19503a908f2SScott Long 			return EBUSY;
1962aedd662SScott Long 		}
1972aedd662SScott Long 		SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
1982aedd662SScott Long 		(sc->used_commands)++;
19903a908f2SScott Long 	} else {
20003a908f2SScott Long 		if (sc->state & IPS_STATIC_BUSY)
20103a908f2SScott Long 			return EAGAIN;
20203a908f2SScott Long 		command = sc->staticcmd;
20303a908f2SScott Long 		sc->state |= IPS_STATIC_BUSY;
20403a908f2SScott Long 	}
2052aedd662SScott Long 	clear_ips_command(command);
2062aedd662SScott Long 	bzero(command->command_buffer, IPS_COMMAND_LEN);
20703a908f2SScott Long 	*cmd = command;
20803a908f2SScott Long 	return 0;
2092aedd662SScott Long }
2102aedd662SScott Long 
2112aedd662SScott Long /* adds a command back to the free command queue */
2122aedd662SScott Long void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command)
2132aedd662SScott Long {
214b234a120SScott Long 
21503a908f2SScott Long 	if (sema_value(&sc->cmd_sema) != 0)
216b234a120SScott Long 		panic("ips: command returned non-zero semaphore");
217b234a120SScott Long 
21803a908f2SScott Long 	if (command != sc->staticcmd) {
2192aedd662SScott Long 		SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
2202aedd662SScott Long 		(sc->used_commands)--;
22103a908f2SScott Long 	} else {
22203a908f2SScott Long 		sc->state &= ~IPS_STATIC_BUSY;
22303a908f2SScott Long 	}
2242aedd662SScott Long }
2257633e7f1SMartin Blapp static const char* ips_diskdev_statename(u_int8_t state)
2267633e7f1SMartin Blapp {
2277633e7f1SMartin Blapp 	static char statebuf[20];
2287633e7f1SMartin Blapp 	switch(state){
2297633e7f1SMartin Blapp 		case IPS_LD_OFFLINE:
2307633e7f1SMartin Blapp 			return("OFFLINE");
2317633e7f1SMartin Blapp 			break;
2327633e7f1SMartin Blapp 		case IPS_LD_OKAY:
2337633e7f1SMartin Blapp 			return("OK");
2347633e7f1SMartin Blapp 			break;
2357633e7f1SMartin Blapp 		case IPS_LD_DEGRADED:
2367633e7f1SMartin Blapp 			return("DEGRADED");
2377633e7f1SMartin Blapp 			break;
2387633e7f1SMartin Blapp 		case IPS_LD_FREE:
2397633e7f1SMartin Blapp 			return("FREE");
2407633e7f1SMartin Blapp 			break;
2417633e7f1SMartin Blapp 		case IPS_LD_SYS:
2427633e7f1SMartin Blapp 			return("SYS");
2437633e7f1SMartin Blapp 			break;
2447633e7f1SMartin Blapp 		case IPS_LD_CRS:
2457633e7f1SMartin Blapp 			return("CRS");
2467633e7f1SMartin Blapp 			break;
2477633e7f1SMartin Blapp 	}
2487633e7f1SMartin Blapp 	sprintf(statebuf,"UNKNOWN(0x%02x)", state);
2497633e7f1SMartin Blapp 	return(statebuf);
2507633e7f1SMartin Blapp }
2512aedd662SScott Long 
2522aedd662SScott Long static int ips_diskdev_init(ips_softc_t *sc)
2532aedd662SScott Long {
2542aedd662SScott Long 	int i;
2552aedd662SScott Long 	for(i=0; i < IPS_MAX_NUM_DRIVES; i++){
2567633e7f1SMartin Blapp 		if(sc->drives[i].state == IPS_LD_FREE) continue;
2577633e7f1SMartin Blapp 		device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n",
2587633e7f1SMartin Blapp 			i, sc->drives[i].raid_lvl,
2597633e7f1SMartin Blapp 			sc->drives[i].sector_count,
2607633e7f1SMartin Blapp 			ips_diskdev_statename(sc->drives[i].state));
2617633e7f1SMartin Blapp 		if(sc->drives[i].state == IPS_LD_OKAY ||
2627633e7f1SMartin Blapp 		   sc->drives[i].state == IPS_LD_DEGRADED){
2632aedd662SScott Long 			sc->diskdev[i] = device_add_child(sc->dev, NULL, -1);
264f472527cSPeter Wemm 			device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i);
2652aedd662SScott Long 		}
2662aedd662SScott Long 	}
2672aedd662SScott Long 	if(bus_generic_attach(sc->dev)){
2682aedd662SScott Long 		device_printf(sc->dev, "Attaching bus failed\n");
2692aedd662SScott Long 	}
2702aedd662SScott Long 	return 0;
2712aedd662SScott Long }
2722aedd662SScott Long 
2732aedd662SScott Long static int ips_diskdev_free(ips_softc_t *sc)
2742aedd662SScott Long {
2752aedd662SScott Long 	int i;
2762aedd662SScott Long 	int error = 0;
2772aedd662SScott Long 	for(i = 0; i < IPS_MAX_NUM_DRIVES; i++){
2782aedd662SScott Long 		if(sc->diskdev[i])
2792aedd662SScott Long 			error = device_delete_child(sc->dev, sc->diskdev[i]);
2802aedd662SScott Long 			if(error)
2812aedd662SScott Long 				return error;
2822aedd662SScott Long 	}
2832aedd662SScott Long 	bus_generic_detach(sc->dev);
2842aedd662SScott Long 	return 0;
2852aedd662SScott Long }
2862aedd662SScott Long 
2872aedd662SScott Long /* ips_timeout is periodically called to make sure no commands sent
2882aedd662SScott Long  * to the card have become stuck.  If it finds a stuck command, it
2892aedd662SScott Long  * sets a flag so the driver won't start any more commands and then
2902aedd662SScott Long  * is periodically called to see if all outstanding commands have
2912aedd662SScott Long  * either finished or timed out.  Once timed out, an attempt to
2922aedd662SScott Long  * reinitialize the card is made.  If that fails, the driver gives
2932aedd662SScott Long  * up and declares the card dead. */
2942aedd662SScott Long static void ips_timeout(void *arg)
2952aedd662SScott Long {
2962aedd662SScott Long 	ips_softc_t *sc = arg;
2972aedd662SScott Long 	int i, state = 0;
2982aedd662SScott Long 	ips_command_t *command;
29903a908f2SScott Long 
30003a908f2SScott Long 	mtx_lock(&sc->queue_mtx);
3012aedd662SScott Long 	command = &sc->commandarray[0];
3022aedd662SScott Long 	for(i = 0; i < sc->max_cmds; i++){
3032aedd662SScott Long 		if(!command[i].timeout){
3042aedd662SScott Long 			continue;
3052aedd662SScott Long 		}
3062aedd662SScott Long 		command[i].timeout--;
3072aedd662SScott Long 		if(!command[i].timeout){
3082aedd662SScott Long 			if(!(sc->state & IPS_TIMEOUT)){
3092aedd662SScott Long 				sc->state |= IPS_TIMEOUT;
3102aedd662SScott Long 				device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n");
3112aedd662SScott Long 			}
3122eea7051SScott Long 			ips_set_error(&command[i], ETIMEDOUT);
3132aedd662SScott Long 			command[i].callback(&command[i]);
3142aedd662SScott Long 			/* hmm, this should be enough cleanup */
3152aedd662SScott Long 		} else
3162aedd662SScott Long 			state = 1;
3172aedd662SScott Long 	}
3182aedd662SScott Long 	if(!state && (sc->state & IPS_TIMEOUT)){
3192aedd662SScott Long 		if(sc->ips_adapter_reinit(sc, 1)){
3202aedd662SScott Long 			device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n");
3212aedd662SScott Long 			sc->state |= IPS_OFFLINE;
3222aedd662SScott Long 			sc->state &= ~IPS_TIMEOUT;
3232aedd662SScott Long 			/* Grr, I hate this solution. I run waiting commands
3242aedd662SScott Long 			   one at a time and error them out just before they
3252aedd662SScott Long 			   would go to the card. This sucks. */
3262aedd662SScott Long 		} else
3272aedd662SScott Long 			sc->state &= ~IPS_TIMEOUT;
3282aedd662SScott Long 	}
3292aedd662SScott Long 	if (sc->state != IPS_OFFLINE)
3302aedd662SScott Long 		sc->timer = timeout(ips_timeout, sc, 10*hz);
33103a908f2SScott Long 	mtx_unlock(&sc->queue_mtx);
3322aedd662SScott Long }
3332aedd662SScott Long 
3342aedd662SScott Long /* check card and initialize it */
3352aedd662SScott Long int ips_adapter_init(ips_softc_t *sc)
3362aedd662SScott Long {
337dea4622dSScott Long         int i;
3382aedd662SScott Long         DEVICE_PRINTF(1,sc->dev, "initializing\n");
339b234a120SScott Long 
3402aedd662SScott Long         if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
3412aedd662SScott Long 				/* alignemnt */	1,
3422aedd662SScott Long 				/* boundary  */	0,
3432aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
3442aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
3452aedd662SScott Long 				/* filter    */	NULL,
3462aedd662SScott Long 				/* filterarg */	NULL,
3472aedd662SScott Long 				/* maxsize   */	IPS_COMMAND_LEN +
3482aedd662SScott Long 						    IPS_MAX_SG_LEN,
3492aedd662SScott Long 				/* numsegs   */	1,
3502aedd662SScott Long 				/* maxsegsize*/	IPS_COMMAND_LEN +
3512aedd662SScott Long 						    IPS_MAX_SG_LEN,
3522aedd662SScott Long 				/* flags     */	0,
35303a908f2SScott Long 				/* lockfunc  */ NULL,
35403a908f2SScott Long 				/* lockarg   */ NULL,
3552aedd662SScott Long 				&sc->command_dmatag) != 0) {
3562aedd662SScott Long                 device_printf(sc->dev, "can't alloc command dma tag\n");
3572aedd662SScott Long 		goto error;
3582aedd662SScott Long         }
3592aedd662SScott Long 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
3602aedd662SScott Long 				/* alignemnt */	1,
3612aedd662SScott Long 				/* boundary  */	0,
3622aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
3632aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
3642aedd662SScott Long 				/* filter    */	NULL,
3652aedd662SScott Long 				/* filterarg */	NULL,
3662aedd662SScott Long 				/* maxsize   */	IPS_MAX_IOBUF_SIZE,
3672aedd662SScott Long 				/* numsegs   */	IPS_MAX_SG_ELEMENTS,
3682aedd662SScott Long 				/* maxsegsize*/	IPS_MAX_IOBUF_SIZE,
3692aedd662SScott Long 				/* flags     */	0,
370f6b1c44dSScott Long 				/* lockfunc  */ busdma_lock_mutex,
37103a908f2SScott Long 				/* lockarg   */ &sc->queue_mtx,
3722aedd662SScott Long 				&sc->sg_dmatag) != 0) {
3732aedd662SScott Long 		device_printf(sc->dev, "can't alloc SG dma tag\n");
3742aedd662SScott Long 		goto error;
3752aedd662SScott Long 	}
3762aedd662SScott Long 	/* create one command buffer until we know how many commands this card
3772aedd662SScott Long            can handle */
3782aedd662SScott Long 	sc->max_cmds = 1;
3792aedd662SScott Long 	ips_cmdqueue_init(sc);
380dea4622dSScott Long 	callout_handle_init(&sc->timer);
3812aedd662SScott Long 
3822aedd662SScott Long 	if(sc->ips_adapter_reinit(sc, 0))
3832aedd662SScott Long 		goto error;
3842aedd662SScott Long 
3857633e7f1SMartin Blapp 	/* initialize ffdc values */
3867633e7f1SMartin Blapp 	microtime(&sc->ffdc_resettime);
3877633e7f1SMartin Blapp 	sc->ffdc_resetcount = 1;
3887633e7f1SMartin Blapp 	if ((i = ips_ffdc_reset(sc)) != 0) {
3897633e7f1SMartin Blapp 		device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i);
3907633e7f1SMartin Blapp 		goto error;
3917633e7f1SMartin Blapp 	}
392dea4622dSScott Long 	if ((i = ips_get_adapter_info(sc)) != 0) {
393dea4622dSScott Long 		device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i);
394dea4622dSScott Long 		goto error;
395dea4622dSScott Long 	}
3967633e7f1SMartin Blapp 	ips_update_nvram(sc); /* no error check as failure doesn't matter */
3977633e7f1SMartin Blapp 	if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){
3987633e7f1SMartin Blapp 		device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]);
3997633e7f1SMartin Blapp 	}
400dea4622dSScott Long  	if ((i = ips_get_drive_info(sc)) != 0) {
401dea4622dSScott Long 		device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i);
4022aedd662SScott Long 		goto error;
4032aedd662SScott Long 	}
4042aedd662SScott Long 
4052aedd662SScott Long         ips_cmdqueue_free(sc);
4062aedd662SScott Long 	if(sc->adapter_info.max_concurrent_cmds)
4072aedd662SScott Long         	sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds);
4082aedd662SScott Long 	else
4092aedd662SScott Long 		sc->max_cmds = 32;
4102aedd662SScott Long         if(ips_cmdqueue_init(sc)){
4112aedd662SScott Long 		device_printf(sc->dev, "failed to initialize command buffers\n");
4122aedd662SScott Long 		goto error;
4132aedd662SScott Long 	}
4142aedd662SScott Long         sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR,
4152aedd662SScott Long                                         S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev));
4162aedd662SScott Long 	sc->device_file->si_drv1 = sc;
4172aedd662SScott Long 	ips_diskdev_init(sc);
4182aedd662SScott Long 	sc->timer = timeout(ips_timeout, sc, 10*hz);
4192aedd662SScott Long         return 0;
4202aedd662SScott Long 
4212aedd662SScott Long error:
4222aedd662SScott Long 	ips_adapter_free(sc);
4232aedd662SScott Long 	return ENXIO;
4242aedd662SScott Long }
4252aedd662SScott Long 
4262aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization */
4272aedd662SScott Long int ips_morpheus_reinit(ips_softc_t *sc, int force)
4282aedd662SScott Long {
4292aedd662SScott Long         u_int32_t tmp;
4302aedd662SScott Long 	int i;
4312aedd662SScott Long 
4322aedd662SScott Long 	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4332aedd662SScott Long 	if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) &&
4342aedd662SScott Long 	    (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){
4352aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
4362aedd662SScott Long 		return 0;
4372aedd662SScott Long 	}
4382aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff);
4392aedd662SScott Long 	ips_read_4(sc, MORPHEUS_REG_OIMR);
4402aedd662SScott Long 
4412aedd662SScott Long 	device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n");
4422aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000);
4432aedd662SScott Long 	DELAY(5000000);
444f94dfeb4SScott Long 	ips_read_4(sc, MORPHEUS_REG_OIMR);
4452aedd662SScott Long 
4462aedd662SScott Long 	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4472aedd662SScott Long 	for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){
4482aedd662SScott Long 		DELAY(1000000);
4492aedd662SScott Long 		DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i);
4502aedd662SScott Long 		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4512aedd662SScott Long 	}
4522aedd662SScott Long 	if(tmp & MORPHEUS_BIT_POST1)
4532aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1);
4542aedd662SScott Long 
4552aedd662SScott Long         if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){
4562aedd662SScott Long                 device_printf(sc->dev,"Adapter error during initialization.\n");
4572aedd662SScott Long 		return 1;
4582aedd662SScott Long         }
4592aedd662SScott Long 	for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){
4602aedd662SScott Long 		DELAY(1000000);
4612aedd662SScott Long 		DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i);
4622aedd662SScott Long 		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4632aedd662SScott Long 	}
4642aedd662SScott Long 	if(tmp & MORPHEUS_BIT_POST2)
4652aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2);
4662aedd662SScott Long 
4672aedd662SScott Long 	if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){
4682aedd662SScott Long 		device_printf(sc->dev, "adapter failed config check\n");
4692aedd662SScott Long 		return 1;
4702aedd662SScott Long         }
4712aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
4722aedd662SScott Long 	if(force && ips_clear_adapter(sc)){
4732aedd662SScott Long 		device_printf(sc->dev, "adapter clear failed\n");
4742aedd662SScott Long 		return 1;
4752aedd662SScott Long 	}
4762aedd662SScott Long 	return 0;
4772aedd662SScott Long }
4782aedd662SScott Long 
4792aedd662SScott Long /* clean up so we can unload the driver. */
4802aedd662SScott Long int ips_adapter_free(ips_softc_t *sc)
4812aedd662SScott Long {
4822aedd662SScott Long 	int error = 0;
4832aedd662SScott Long 	if(sc->state & IPS_DEV_OPEN)
4842aedd662SScott Long 		return EBUSY;
4852aedd662SScott Long 	if((error = ips_diskdev_free(sc)))
4862aedd662SScott Long 		return error;
4872aedd662SScott Long 	if(ips_cmdqueue_free(sc)){
4882aedd662SScott Long 		device_printf(sc->dev,
4892aedd662SScott Long 		     "trying to exit when command queue is not empty!\n");
4902aedd662SScott Long 		return EBUSY;
4912aedd662SScott Long 	}
4922aedd662SScott Long 	DEVICE_PRINTF(1, sc->dev, "free\n");
4932aedd662SScott Long 	untimeout(ips_timeout, sc, sc->timer);
4942aedd662SScott Long 
4952aedd662SScott Long 	if(sc->sg_dmatag)
4962aedd662SScott Long 		bus_dma_tag_destroy(sc->sg_dmatag);
4972aedd662SScott Long 	if(sc->command_dmatag)
4982aedd662SScott Long 		bus_dma_tag_destroy(sc->command_dmatag);
4992aedd662SScott Long 	if(sc->device_file)
5002aedd662SScott Long 	        destroy_dev(sc->device_file);
5012aedd662SScott Long         return 0;
5022aedd662SScott Long }
5032aedd662SScott Long 
5047765040eSScott Long static __inline int ips_morpheus_check_intr(ips_softc_t *sc)
5052aedd662SScott Long {
5062aedd662SScott Long 	int cmdnumber;
5072aedd662SScott Long 	ips_cmd_status_t status;
5087765040eSScott Long 	ips_command_t *command;
5097765040eSScott Long 	int found = 0;
5107765040eSScott Long 	u_int32_t oisr;
5112aedd662SScott Long 
5122aedd662SScott Long 	oisr = ips_read_4(sc, MORPHEUS_REG_OISR);
5137765040eSScott Long 	PRINTF(9, "interrupt registers out:%x\n", oisr);
5142aedd662SScott Long 	if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){
5152aedd662SScott Long 		DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n");
5167765040eSScott Long 		return (0);
5172aedd662SScott Long 	}
5182aedd662SScott Long 	while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){
5192aedd662SScott Long 		cmdnumber = status.fields.command_id;
5207765040eSScott Long 		command = &sc->commandarray[cmdnumber];
5217765040eSScott Long 		command->status.value = status.value;
5227765040eSScott Long 		command->timeout = 0;
5237765040eSScott Long 		command->callback(command);
5242aedd662SScott Long 
5257765040eSScott Long 		found = 1;
5262aedd662SScott Long 	}
5277765040eSScott Long         return (found);
5287765040eSScott Long }
5297765040eSScott Long 
5307765040eSScott Long void ips_morpheus_intr(void *void_sc)
5317765040eSScott Long {
5327765040eSScott Long 	ips_softc_t *sc = void_sc;
5337765040eSScott Long 
5347765040eSScott Long 	mtx_lock(&sc->queue_mtx);
5357765040eSScott Long 	ips_morpheus_check_intr(sc);
53603a908f2SScott Long 	mtx_unlock(&sc->queue_mtx);
5377765040eSScott Long }
5387765040eSScott Long 
5397765040eSScott Long void ips_morpheus_poll(ips_command_t *command)
5407765040eSScott Long {
5417765040eSScott Long 	uint32_t ts;
5427765040eSScott Long 
5434ef63badSScott Long 	/*
5444ef63badSScott Long 	 * Locks are not used here because this is only called during
5454ef63badSScott Long 	 * crashdumps.
5464ef63badSScott Long 	 */
5477765040eSScott Long 	ts = time_second + command->timeout;
5487765040eSScott Long 	while ((command->timeout != 0)
5497765040eSScott Long 	 && (ips_morpheus_check_intr(command->sc) == 0)
5507765040eSScott Long 	 && (ts > time_second))
5517765040eSScott Long 		DELAY(1000);
5522aedd662SScott Long }
5532aedd662SScott Long 
5542aedd662SScott Long void ips_issue_morpheus_cmd(ips_command_t *command)
5552aedd662SScott Long {
5562aedd662SScott Long 	/* hmmm, is there a cleaner way to do this? */
5572aedd662SScott Long 	if(command->sc->state & IPS_OFFLINE){
5582eea7051SScott Long 		ips_set_error(command, EINVAL);
5592aedd662SScott Long 		command->callback(command);
5602aedd662SScott Long 		return;
5612aedd662SScott Long 	}
5622aedd662SScott Long 	command->timeout = 10;
5632aedd662SScott Long 	ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr);
5642aedd662SScott Long }
5652aedd662SScott Long 
5662aedd662SScott Long static void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error)
5672aedd662SScott Long {
5682aedd662SScott Long 	ips_copper_queue_t *queue = queueptr;
5692aedd662SScott Long 	if(error){
5702aedd662SScott Long 		return;
5712aedd662SScott Long 	}
5722aedd662SScott Long 	queue->base_phys_addr = segments[0].ds_addr;
5732aedd662SScott Long }
5742aedd662SScott Long 
5752aedd662SScott Long static int ips_copperhead_queue_init(ips_softc_t *sc)
5762aedd662SScott Long {
5772aedd662SScott Long 	int error;
5782aedd662SScott Long 	bus_dma_tag_t dmatag;
5792aedd662SScott Long 	bus_dmamap_t dmamap;
5802aedd662SScott Long        	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
5812aedd662SScott Long 				/* alignemnt */	1,
5822aedd662SScott Long 				/* boundary  */	0,
5832aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
5842aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
5852aedd662SScott Long 				/* filter    */	NULL,
5862aedd662SScott Long 				/* filterarg */	NULL,
5872aedd662SScott Long 				/* maxsize   */	sizeof(ips_copper_queue_t),
5882aedd662SScott Long 				/* numsegs   */	1,
5892aedd662SScott Long 				/* maxsegsize*/	sizeof(ips_copper_queue_t),
5902aedd662SScott Long 				/* flags     */	0,
59103a908f2SScott Long 				/* lockfunc  */ NULL,
59203a908f2SScott Long 				/* lockarg   */ NULL,
5932aedd662SScott Long 				&dmatag) != 0) {
5942aedd662SScott Long                 device_printf(sc->dev, "can't alloc dma tag for statue queue\n");
5952aedd662SScott Long 		error = ENOMEM;
5962aedd662SScott Long 		goto exit;
5972aedd662SScott Long         }
5982aedd662SScott Long 	if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue),
5992aedd662SScott Long 	   		    BUS_DMA_NOWAIT, &dmamap)){
6002aedd662SScott Long 		error = ENOMEM;
6012aedd662SScott Long 		goto exit;
6022aedd662SScott Long 	}
6032aedd662SScott Long 	bzero(sc->copper_queue, sizeof(ips_copper_queue_t));
6042aedd662SScott Long 	sc->copper_queue->dmatag = dmatag;
6052aedd662SScott Long 	sc->copper_queue->dmamap = dmamap;
6062aedd662SScott Long 	sc->copper_queue->nextstatus = 1;
6072aedd662SScott Long 	bus_dmamap_load(dmatag, dmamap,
6082aedd662SScott Long 			&(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4,
6092aedd662SScott Long 			ips_copperhead_queue_callback, sc->copper_queue,
6102aedd662SScott Long 			BUS_DMA_NOWAIT);
6112aedd662SScott Long 	if(sc->copper_queue->base_phys_addr == 0){
6122aedd662SScott Long 		error = ENOMEM;
6132aedd662SScott Long 		goto exit;
6142aedd662SScott Long 	}
6152aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr);
6162aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr +
6172aedd662SScott Long 		    IPS_MAX_CMD_NUM * 4);
6182aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4);
6192aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr);
6202aedd662SScott Long 
6212aedd662SScott Long 
6222aedd662SScott Long 	return 0;
6232aedd662SScott Long exit:
6242aedd662SScott Long 	bus_dmamem_free(dmatag, sc->copper_queue, dmamap);
6252aedd662SScott Long 	bus_dma_tag_destroy(dmatag);
6262aedd662SScott Long 	return error;
6272aedd662SScott Long }
6282aedd662SScott Long 
6292aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */
6302aedd662SScott Long int ips_copperhead_reinit(ips_softc_t *sc, int force)
6312aedd662SScott Long {
6322aedd662SScott Long 	int i, j;
6332aedd662SScott Long 	u_int32_t postcode = 0, configstatus = 0;
6342aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, 0x80);
6352aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, 0);
6362aedd662SScott Long 	device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n");
6372aedd662SScott Long 	for(j = 0; j < 2; j++){
6382aedd662SScott Long 		postcode <<= 8;
6392aedd662SScott Long 		for(i = 0; i < 45; i++){
6402aedd662SScott Long 			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
6412aedd662SScott Long 				postcode |= ips_read_1(sc, COPPER_REG_ISPR);
6422aedd662SScott Long 				ips_write_1(sc, COPPER_REG_HISR,
6432aedd662SScott Long 					    COPPER_GHI_BIT);
6442aedd662SScott Long 				break;
6452aedd662SScott Long 			} else
6462aedd662SScott Long 				DELAY(1000000);
6472aedd662SScott Long 		}
6482aedd662SScott Long 		if(i == 45)
6492aedd662SScott Long 			return 1;
6502aedd662SScott Long 	}
6512aedd662SScott Long 	for(j = 0; j < 2; j++){
6522aedd662SScott Long 		configstatus <<= 8;
6532aedd662SScott Long 		for(i = 0; i < 240; i++){
6542aedd662SScott Long 			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
6552aedd662SScott Long 				configstatus |= ips_read_1(sc, COPPER_REG_ISPR);
6562aedd662SScott Long 				ips_write_1(sc, COPPER_REG_HISR,
6572aedd662SScott Long 					    COPPER_GHI_BIT);
6582aedd662SScott Long 				break;
6592aedd662SScott Long 			} else
6602aedd662SScott Long 				DELAY(1000000);
6612aedd662SScott Long 		}
6622aedd662SScott Long 		if(i == 240)
6632aedd662SScott Long 			return 1;
6642aedd662SScott Long 	}
6652aedd662SScott Long 	for(i = 0; i < 240; i++){
6662aedd662SScott Long 		if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){
6672aedd662SScott Long 			break;
6682aedd662SScott Long 		} else
6692aedd662SScott Long 			DELAY(1000000);
6702aedd662SScott Long 	}
6712aedd662SScott Long 	if(i == 240)
6722aedd662SScott Long 		return 1;
6732aedd662SScott Long 	ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT);
6742aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT);
6752aedd662SScott Long 	ips_copperhead_queue_init(sc);
6762aedd662SScott Long 	ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT);
6772aedd662SScott Long 	i = ips_read_1(sc, COPPER_REG_SCPR);
6782aedd662SScott Long 	ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT);
6792aedd662SScott Long 	if(!configstatus){
6802aedd662SScott Long 		device_printf(sc->dev, "adapter initialization failed\n");
6812aedd662SScott Long 		return 1;
6822aedd662SScott Long 	}
6832aedd662SScott Long 	if(force && ips_clear_adapter(sc)){
6842aedd662SScott Long 		device_printf(sc->dev, "adapter clear failed\n");
6852aedd662SScott Long 		return 1;
6862aedd662SScott Long 	}
6872aedd662SScott Long 	return 0;
6882aedd662SScott Long }
6892aedd662SScott Long static u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc)
6902aedd662SScott Long {
6912aedd662SScott Long 	u_int32_t value;
6922aedd662SScott Long 	int statnum = sc->copper_queue->nextstatus++;
6932aedd662SScott Long 	if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM)
6942aedd662SScott Long 		sc->copper_queue->nextstatus = 0;
6952aedd662SScott Long 	value = sc->copper_queue->status[statnum];
6962aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr +
6972aedd662SScott Long 		    4 * statnum);
6982aedd662SScott Long 	return value;
6992aedd662SScott Long }
7002aedd662SScott Long 
7012aedd662SScott Long 
7022aedd662SScott Long void ips_copperhead_intr(void *void_sc)
7032aedd662SScott Long {
7042aedd662SScott Long         ips_softc_t *sc = (ips_softc_t *)void_sc;
7052aedd662SScott Long 	int cmdnumber;
7062aedd662SScott Long 	ips_cmd_status_t status;
7072aedd662SScott Long 
70803a908f2SScott Long 	mtx_lock(&sc->queue_mtx);
7092aedd662SScott Long 	while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){
7102aedd662SScott Long 		status.value = ips_copperhead_cmd_status(sc);
7112aedd662SScott Long 		cmdnumber = status.fields.command_id;
7122aedd662SScott Long 		sc->commandarray[cmdnumber].status.value = status.value;
7132aedd662SScott Long 		sc->commandarray[cmdnumber].timeout = 0;
7142aedd662SScott Long 		sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
7152aedd662SScott Long 		PRINTF(9, "ips: got command %d\n", cmdnumber);
7162aedd662SScott Long 	}
71703a908f2SScott Long 	mtx_unlock(&sc->queue_mtx);
7182aedd662SScott Long         return;
7192aedd662SScott Long }
7202aedd662SScott Long 
7212aedd662SScott Long void ips_issue_copperhead_cmd(ips_command_t *command)
7222aedd662SScott Long {
7232aedd662SScott Long 	int i;
7242aedd662SScott Long 	/* hmmm, is there a cleaner way to do this? */
7252aedd662SScott Long 	if(command->sc->state & IPS_OFFLINE){
7262eea7051SScott Long 		ips_set_error(command, EINVAL);
7272aedd662SScott Long 		command->callback(command);
7282aedd662SScott Long 		return;
7292aedd662SScott Long 	}
7302aedd662SScott Long 	command->timeout = 10;
7312aedd662SScott Long 	for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT;
7322aedd662SScott Long 	    i++ ){
7332aedd662SScott Long 		if( i == 20){
7342aedd662SScott Long printf("sem bit still set, can't send a command\n");
7352aedd662SScott Long 			return;
7362aedd662SScott Long 		}
7372aedd662SScott Long 		DELAY(500);/* need to do a delay here */
7382aedd662SScott Long 	}
7392aedd662SScott Long 	ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr);
7402aedd662SScott Long 	ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START);
7412aedd662SScott Long }
7422aedd662SScott Long 
7437765040eSScott Long void ips_copperhead_poll(ips_command_t *command)
7447765040eSScott Long {
7457765040eSScott Long 
7467765040eSScott Long 	printf("ips: cmd polling not implemented for copperhead devices\n");
7477765040eSScott Long }
748