xref: /freebsd/sys/dev/ips/ips.c (revision dd83a01e81b7f4729a40700faf8ae29b254878b4)
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 
312aedd662SScott Long #include <dev/ips/ips.h>
322aedd662SScott Long #include <sys/stat.h>
332aedd662SScott Long #include <sys/time.h>
342aedd662SScott Long #include <machine/clock.h>
352aedd662SScott Long 
362aedd662SScott Long static d_open_t ips_open;
372aedd662SScott Long static d_close_t ips_close;
382aedd662SScott Long static d_ioctl_t ips_ioctl;
392aedd662SScott Long 
40dd83a01eSScott Long MALLOC_DEFINE(M_IPSBUF, "ipsbuf","IPS driver buffer");
41dd83a01eSScott Long 
422aedd662SScott Long static struct cdevsw ips_cdevsw = {
43dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
44dc08ffecSPoul-Henning Kamp 	.d_flags =	D_NEEDGIANT,
452aedd662SScott Long 	.d_open =	ips_open,
462aedd662SScott Long 	.d_close =	ips_close,
472aedd662SScott Long 	.d_ioctl =	ips_ioctl,
482aedd662SScott Long 	.d_name =	"ips",
492aedd662SScott Long };
502aedd662SScott Long 
517633e7f1SMartin Blapp static const char* ips_adapter_name[] = {
527633e7f1SMartin Blapp 	"N/A",
537633e7f1SMartin Blapp 	"ServeRAID (copperhead)",
547633e7f1SMartin Blapp 	"ServeRAID II (copperhead refresh)",
557633e7f1SMartin Blapp 	"ServeRAID onboard (copperhead)",
567633e7f1SMartin Blapp 	"ServeRAID onboard (copperhead)",
577633e7f1SMartin Blapp 	"ServeRAID 3H (clarinet)",
587633e7f1SMartin Blapp 	"ServeRAID 3L (clarinet lite)",
597633e7f1SMartin Blapp 	"ServeRAID 4H (trombone)",
607633e7f1SMartin Blapp 	"ServeRAID 4M (morpheus)",
617633e7f1SMartin Blapp 	"ServeRAID 4L (morpheus lite)",
627633e7f1SMartin Blapp 	"ServeRAID 4Mx (neo)",
637633e7f1SMartin Blapp 	"ServeRAID 4Lx (neo lite)",
647633e7f1SMartin Blapp 	"ServeRAID 5i II (sarasota)",
657633e7f1SMartin Blapp 	"ServeRAID 5i (sarasota)",
667633e7f1SMartin Blapp 	"ServeRAID 6M (marco)",
677633e7f1SMartin Blapp 	"ServeRAID 6i (sebring)"
687633e7f1SMartin Blapp };
697633e7f1SMartin Blapp 
702aedd662SScott Long 
712aedd662SScott Long static int ips_open(dev_t dev, int flags, int fmt, struct thread *td)
722aedd662SScott Long {
732aedd662SScott Long 	ips_softc_t *sc = dev->si_drv1;
742aedd662SScott Long 	sc->state |= IPS_DEV_OPEN;
752aedd662SScott Long         return 0;
762aedd662SScott Long }
772aedd662SScott Long 
782aedd662SScott Long static int ips_close(dev_t dev, int flags, int fmt, struct thread *td)
792aedd662SScott Long {
802aedd662SScott Long 	ips_softc_t *sc = dev->si_drv1;
812aedd662SScott Long 	sc->state &= ~IPS_DEV_OPEN;
822aedd662SScott Long 
832aedd662SScott Long         return 0;
842aedd662SScott Long }
852aedd662SScott Long 
862aedd662SScott Long static int ips_ioctl(dev_t dev, u_long command, caddr_t addr, int32_t flags, struct thread *td)
872aedd662SScott Long {
882aedd662SScott Long 	ips_softc_t *sc;
892aedd662SScott Long 
902aedd662SScott Long 	sc = dev->si_drv1;
912aedd662SScott Long 	return ips_ioctl_request(sc, command, addr, flags);
922aedd662SScott Long }
932aedd662SScott Long 
942aedd662SScott Long static void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
952aedd662SScott Long {
962aedd662SScott Long 	ips_command_t *command = cmdptr;
972aedd662SScott Long 	PRINTF(10, "ips: in ips_cmd_dmaload\n");
982aedd662SScott Long 	if(!error)
992aedd662SScott Long 		command->command_phys_addr = segments[0].ds_addr;
1002aedd662SScott Long 
1012aedd662SScott Long }
1022aedd662SScott Long 
1032aedd662SScott Long /* is locking needed? what locking guarentees are there on removal? */
1042aedd662SScott Long static __inline__ int ips_cmdqueue_free(ips_softc_t *sc)
1052aedd662SScott Long {
1062aedd662SScott Long 	int i, error = -1;
107b234a120SScott Long 	ips_command_t *command;
108b234a120SScott Long 	intrmask_t mask;
109b234a120SScott Long 
110b234a120SScott Long 	mask = splbio();
1112aedd662SScott Long 	if(!sc->used_commands){
1122aedd662SScott Long 		for(i = 0; i < sc->max_cmds; i++){
113b234a120SScott Long 
114b234a120SScott Long 			command = &sc->commandarray[i];
115b234a120SScott Long 			sema_destroy(&command->cmd_sema);
116b234a120SScott Long 
117b234a120SScott Long 			if(command->command_phys_addr == 0)
1182aedd662SScott Long 				continue;
1192aedd662SScott Long 			bus_dmamap_unload(sc->command_dmatag,
120b234a120SScott Long 					  command->command_dmamap);
1212aedd662SScott Long 			bus_dmamem_free(sc->command_dmatag,
122b234a120SScott Long 					command->command_buffer,
123b234a120SScott Long 					command->command_dmamap);
1242aedd662SScott Long 		}
1252aedd662SScott Long 		error = 0;
1262aedd662SScott Long 		sc->state |= IPS_OFFLINE;
1272aedd662SScott Long 	}
1282aedd662SScott Long 	splx(mask);
1292aedd662SScott Long 	return error;
1302aedd662SScott Long }
1312aedd662SScott Long 
1322aedd662SScott Long /* places all ips command structs on the free command queue.  No locking as if someone else tries
1332aedd662SScott Long  * to access this during init, we have bigger problems */
1342aedd662SScott Long static __inline__ int ips_cmdqueue_init(ips_softc_t *sc)
1352aedd662SScott Long {
1362aedd662SScott Long 	int i;
1372aedd662SScott Long 	ips_command_t *command;
1382aedd662SScott Long 	SLIST_INIT(&sc->free_cmd_list);
1392aedd662SScott Long 	STAILQ_INIT(&sc->cmd_wait_list);
1402aedd662SScott Long 	for(i = 0; i < sc->max_cmds; i++){
1412aedd662SScott Long 		command = &sc->commandarray[i];
142b234a120SScott Long 		command->id = i;
143b234a120SScott Long 		command->sc = sc;
144b234a120SScott Long 
1452aedd662SScott Long 		if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer,
1462aedd662SScott Long 		    BUS_DMA_NOWAIT, &command->command_dmamap))
1472aedd662SScott Long 			goto error;
1482aedd662SScott Long 		bus_dmamap_load(sc->command_dmatag, command->command_dmamap,
1492aedd662SScott Long 				command->command_buffer,IPS_COMMAND_LEN,
1502aedd662SScott Long 				ips_cmd_dmaload, command, BUS_DMA_NOWAIT);
1512aedd662SScott Long 		if(!command->command_phys_addr){
1522aedd662SScott Long 			bus_dmamem_free(sc->command_dmatag,
1532aedd662SScott Long 			    command->command_buffer, command->command_dmamap);
1542aedd662SScott Long 			goto error;
1552aedd662SScott Long 		}
156b234a120SScott Long 
157b234a120SScott Long 		sema_init(&command->cmd_sema, 0, "IPS Command Semaphore");
158b234a120SScott Long 		SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
1592aedd662SScott Long 	}
1602aedd662SScott Long 	sc->state &= ~IPS_OFFLINE;
1612aedd662SScott Long 	return 0;
1622aedd662SScott Long error:
1632aedd662SScott Long 	ips_cmdqueue_free(sc);
1642aedd662SScott Long 	return ENOMEM;
1652aedd662SScott Long }
1662aedd662SScott Long 
1672aedd662SScott Long static int ips_add_waiting_command(ips_softc_t *sc, int (*callback)(ips_command_t *), void *data, unsigned long flags)
1682aedd662SScott Long {
1692aedd662SScott Long 	intrmask_t mask;
1702aedd662SScott Long 	ips_command_t *command;
1712aedd662SScott Long 	ips_wait_list_t *waiter;
1722aedd662SScott Long 	unsigned long memflags = 0;
1732aedd662SScott Long 	if(IPS_NOWAIT_FLAG & flags)
1742aedd662SScott Long 		memflags = M_NOWAIT;
175dd83a01eSScott Long 	waiter = malloc(sizeof(ips_wait_list_t), M_IPSBUF, memflags);
1762aedd662SScott Long 	if(!waiter)
1772aedd662SScott Long 		return ENOMEM;
1782aedd662SScott Long 	mask = splbio();
1792aedd662SScott Long 	if(sc->state & IPS_OFFLINE){
1802aedd662SScott Long 		splx(mask);
181dd83a01eSScott Long 		free(waiter, M_IPSBUF);
1822aedd662SScott Long 		return EIO;
1832aedd662SScott Long 	}
1842aedd662SScott Long 	command = SLIST_FIRST(&sc->free_cmd_list);
1852aedd662SScott Long 	if(command && !(sc->state & IPS_TIMEOUT)){
1862aedd662SScott Long 		SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
1872aedd662SScott Long 		(sc->used_commands)++;
1882aedd662SScott Long 		splx(mask);
1892aedd662SScott Long 		clear_ips_command(command);
1902aedd662SScott Long 		bzero(command->command_buffer, IPS_COMMAND_LEN);
191dd83a01eSScott Long 		free(waiter, M_IPSBUF);
1922aedd662SScott Long 		command->arg = data;
1932aedd662SScott Long 		return callback(command);
1942aedd662SScott Long 	}
1952aedd662SScott Long 	DEVICE_PRINTF(1, sc->dev, "adding command to the wait queue\n");
1962aedd662SScott Long 	waiter->callback = callback;
1972aedd662SScott Long 	waiter->data = data;
1982aedd662SScott Long 	STAILQ_INSERT_TAIL(&sc->cmd_wait_list, waiter, next);
1992aedd662SScott Long 	splx(mask);
2002aedd662SScott Long 	return 0;
2012aedd662SScott Long }
2022aedd662SScott Long 
2032aedd662SScott Long static void ips_run_waiting_command(ips_softc_t *sc)
2042aedd662SScott Long {
2052aedd662SScott Long 	ips_wait_list_t *waiter;
2062aedd662SScott Long 	ips_command_t	*command;
2072aedd662SScott Long 	int (*callback)(ips_command_t*);
2082aedd662SScott Long 	intrmask_t mask;
2092aedd662SScott Long 
2102aedd662SScott Long 	mask = splbio();
2112aedd662SScott Long 	waiter = STAILQ_FIRST(&sc->cmd_wait_list);
2122aedd662SScott Long 	command = SLIST_FIRST(&sc->free_cmd_list);
2132aedd662SScott Long 	if(!waiter || !command){
2142aedd662SScott Long 		splx(mask);
2152aedd662SScott Long 		return;
2162aedd662SScott Long 	}
2172aedd662SScott Long 	DEVICE_PRINTF(1, sc->dev, "removing command from wait queue\n");
2182aedd662SScott Long 	SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
2192aedd662SScott Long 	STAILQ_REMOVE_HEAD(&sc->cmd_wait_list, next);
2202aedd662SScott Long 	(sc->used_commands)++;
2212aedd662SScott Long 	splx(mask);
2222aedd662SScott Long 	clear_ips_command(command);
2232aedd662SScott Long 	bzero(command->command_buffer, IPS_COMMAND_LEN);
2242aedd662SScott Long 	command->arg = waiter->data;
2252aedd662SScott Long 	callback = waiter->callback;
226dd83a01eSScott Long 	free(waiter, M_IPSBUF);
2272aedd662SScott Long 	callback(command);
2282aedd662SScott Long 	return;
2292aedd662SScott Long }
2302aedd662SScott Long /* returns a free command struct if one is available.
2312aedd662SScott Long  * It also blanks out anything that may be a wild pointer/value.
2322aedd662SScott Long  * Also, command buffers are not freed.  They are
2332aedd662SScott Long  * small so they are saved and kept dmamapped and loaded.
2342aedd662SScott Long  */
2352aedd662SScott Long int ips_get_free_cmd(ips_softc_t *sc, int (*callback)(ips_command_t *), void *data, unsigned long flags)
2362aedd662SScott Long {
2372aedd662SScott Long 	intrmask_t mask;
2382aedd662SScott Long 	ips_command_t *command;
2392aedd662SScott Long 	mask = splbio();
2402aedd662SScott Long 
2412aedd662SScott Long 	if(sc->state & IPS_OFFLINE){
2422aedd662SScott Long 		splx(mask);
2432aedd662SScott Long 		return EIO;
2442aedd662SScott Long 	}
2452aedd662SScott Long 	command = SLIST_FIRST(&sc->free_cmd_list);
2462aedd662SScott Long 	if(!command || (sc->state & IPS_TIMEOUT)){
2472aedd662SScott Long 		splx(mask);
2482aedd662SScott Long 		if(flags & IPS_NOWAIT_FLAG)
2492aedd662SScott Long 			return EAGAIN;
2502aedd662SScott Long 		return ips_add_waiting_command(sc, callback, data, flags);
2512aedd662SScott Long 	}
2522aedd662SScott Long 	SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
2532aedd662SScott Long 	(sc->used_commands)++;
2542aedd662SScott Long 	splx(mask);
2552aedd662SScott Long 	clear_ips_command(command);
2562aedd662SScott Long 	bzero(command->command_buffer, IPS_COMMAND_LEN);
2572aedd662SScott Long 	command->arg = data;
2582aedd662SScott Long 	return callback(command);
2592aedd662SScott Long }
2602aedd662SScott Long 
2612aedd662SScott Long /* adds a command back to the free command queue */
2622aedd662SScott Long void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command)
2632aedd662SScott Long {
2642aedd662SScott Long 	intrmask_t mask;
2652aedd662SScott Long 	mask = splbio();
266b234a120SScott Long 
267b234a120SScott Long 	if (sema_value(&command->cmd_sema) != 0)
268b234a120SScott Long 		panic("ips: command returned non-zero semaphore");
269b234a120SScott Long 
2702aedd662SScott Long 	SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
2712aedd662SScott Long 	(sc->used_commands)--;
2722aedd662SScott Long 	splx(mask);
2732aedd662SScott Long 	if(!(sc->state & IPS_TIMEOUT))
2742aedd662SScott Long 		ips_run_waiting_command(sc);
2752aedd662SScott Long }
2767633e7f1SMartin Blapp static const char* ips_diskdev_statename(u_int8_t state)
2777633e7f1SMartin Blapp {
2787633e7f1SMartin Blapp 	static char statebuf[20];
2797633e7f1SMartin Blapp 	switch(state){
2807633e7f1SMartin Blapp 		case IPS_LD_OFFLINE:
2817633e7f1SMartin Blapp 			return("OFFLINE");
2827633e7f1SMartin Blapp 			break;
2837633e7f1SMartin Blapp 		case IPS_LD_OKAY:
2847633e7f1SMartin Blapp 			return("OK");
2857633e7f1SMartin Blapp 			break;
2867633e7f1SMartin Blapp 		case IPS_LD_DEGRADED:
2877633e7f1SMartin Blapp 			return("DEGRADED");
2887633e7f1SMartin Blapp 			break;
2897633e7f1SMartin Blapp 		case IPS_LD_FREE:
2907633e7f1SMartin Blapp 			return("FREE");
2917633e7f1SMartin Blapp 			break;
2927633e7f1SMartin Blapp 		case IPS_LD_SYS:
2937633e7f1SMartin Blapp 			return("SYS");
2947633e7f1SMartin Blapp 			break;
2957633e7f1SMartin Blapp 		case IPS_LD_CRS:
2967633e7f1SMartin Blapp 			return("CRS");
2977633e7f1SMartin Blapp 			break;
2987633e7f1SMartin Blapp 	}
2997633e7f1SMartin Blapp 	sprintf(statebuf,"UNKNOWN(0x%02x)", state);
3007633e7f1SMartin Blapp 	return(statebuf);
3017633e7f1SMartin Blapp }
3022aedd662SScott Long 
3032aedd662SScott Long static int ips_diskdev_init(ips_softc_t *sc)
3042aedd662SScott Long {
3052aedd662SScott Long 	int i;
3062aedd662SScott Long 	for(i=0; i < IPS_MAX_NUM_DRIVES; i++){
3077633e7f1SMartin Blapp 		if(sc->drives[i].state == IPS_LD_FREE) continue;
3087633e7f1SMartin Blapp 		device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n",
3097633e7f1SMartin Blapp 			i, sc->drives[i].raid_lvl,
3107633e7f1SMartin Blapp 			sc->drives[i].sector_count,
3117633e7f1SMartin Blapp 			ips_diskdev_statename(sc->drives[i].state));
3127633e7f1SMartin Blapp 		if(sc->drives[i].state == IPS_LD_OKAY ||
3137633e7f1SMartin Blapp 		   sc->drives[i].state == IPS_LD_DEGRADED){
3142aedd662SScott Long 			sc->diskdev[i] = device_add_child(sc->dev, NULL, -1);
315f472527cSPeter Wemm 			device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i);
3162aedd662SScott Long 		}
3172aedd662SScott Long 	}
3182aedd662SScott Long 	if(bus_generic_attach(sc->dev)){
3192aedd662SScott Long 		device_printf(sc->dev, "Attaching bus failed\n");
3202aedd662SScott Long 	}
3212aedd662SScott Long 	return 0;
3222aedd662SScott Long }
3232aedd662SScott Long 
3242aedd662SScott Long static int ips_diskdev_free(ips_softc_t *sc)
3252aedd662SScott Long {
3262aedd662SScott Long 	int i;
3272aedd662SScott Long 	int error = 0;
3282aedd662SScott Long 	for(i = 0; i < IPS_MAX_NUM_DRIVES; i++){
3292aedd662SScott Long 		if(sc->diskdev[i])
3302aedd662SScott Long 			error = device_delete_child(sc->dev, sc->diskdev[i]);
3312aedd662SScott Long 			if(error)
3322aedd662SScott Long 				return error;
3332aedd662SScott Long 	}
3342aedd662SScott Long 	bus_generic_detach(sc->dev);
3352aedd662SScott Long 	return 0;
3362aedd662SScott Long }
3372aedd662SScott Long 
3382aedd662SScott Long /* ips_timeout is periodically called to make sure no commands sent
3392aedd662SScott Long  * to the card have become stuck.  If it finds a stuck command, it
3402aedd662SScott Long  * sets a flag so the driver won't start any more commands and then
3412aedd662SScott Long  * is periodically called to see if all outstanding commands have
3422aedd662SScott Long  * either finished or timed out.  Once timed out, an attempt to
3432aedd662SScott Long  * reinitialize the card is made.  If that fails, the driver gives
3442aedd662SScott Long  * up and declares the card dead. */
3452aedd662SScott Long static void ips_timeout(void *arg)
3462aedd662SScott Long {
3472aedd662SScott Long 	intrmask_t mask;
3482aedd662SScott Long 	ips_softc_t *sc = arg;
3492aedd662SScott Long 	int i, state = 0;
3502aedd662SScott Long 	ips_command_t *command;
3512aedd662SScott Long 	command = &sc->commandarray[0];
3522aedd662SScott Long 	mask = splbio();
3532aedd662SScott Long 	for(i = 0; i < sc->max_cmds; i++){
3542aedd662SScott Long 		if(!command[i].timeout){
3552aedd662SScott Long 			continue;
3562aedd662SScott Long 		}
3572aedd662SScott Long 		command[i].timeout--;
3582aedd662SScott Long 		if(!command[i].timeout){
3592aedd662SScott Long 			if(!(sc->state & IPS_TIMEOUT)){
3602aedd662SScott Long 				sc->state |= IPS_TIMEOUT;
3612aedd662SScott Long 				device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n");
3622aedd662SScott Long 			}
3632aedd662SScott Long 			command[i].status.value = IPS_ERROR_STATUS;
3642aedd662SScott Long 			command[i].callback(&command[i]);
3652aedd662SScott Long 			/* hmm, this should be enough cleanup */
3662aedd662SScott Long 		} else
3672aedd662SScott Long 			state = 1;
3682aedd662SScott Long 	}
3692aedd662SScott Long 	if(!state && (sc->state & IPS_TIMEOUT)){
3702aedd662SScott Long 		if(sc->ips_adapter_reinit(sc, 1)){
3712aedd662SScott Long 			device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n");
3722aedd662SScott Long 			sc->state |= IPS_OFFLINE;
3732aedd662SScott Long 			sc->state &= ~IPS_TIMEOUT;
3742aedd662SScott Long 			/* Grr, I hate this solution. I run waiting commands
3752aedd662SScott Long 			   one at a time and error them out just before they
3762aedd662SScott Long 			   would go to the card. This sucks. */
3772aedd662SScott Long 		} else
3782aedd662SScott Long 			sc->state &= ~IPS_TIMEOUT;
3792aedd662SScott Long 		ips_run_waiting_command(sc);
3802aedd662SScott Long 	}
3812aedd662SScott Long 	if (sc->state != IPS_OFFLINE)
3822aedd662SScott Long 		sc->timer = timeout(ips_timeout, sc, 10*hz);
3832aedd662SScott Long 	splx(mask);
3842aedd662SScott Long }
3852aedd662SScott Long 
3862aedd662SScott Long /* check card and initialize it */
3872aedd662SScott Long int ips_adapter_init(ips_softc_t *sc)
3882aedd662SScott Long {
389dea4622dSScott Long         int i;
3902aedd662SScott Long         DEVICE_PRINTF(1,sc->dev, "initializing\n");
391b234a120SScott Long 
3922aedd662SScott Long         if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
3932aedd662SScott Long 				/* alignemnt */	1,
3942aedd662SScott Long 				/* boundary  */	0,
3952aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
3962aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
3972aedd662SScott Long 				/* filter    */	NULL,
3982aedd662SScott Long 				/* filterarg */	NULL,
3992aedd662SScott Long 				/* maxsize   */	IPS_COMMAND_LEN +
4002aedd662SScott Long 						    IPS_MAX_SG_LEN,
4012aedd662SScott Long 				/* numsegs   */	1,
4022aedd662SScott Long 				/* maxsegsize*/	IPS_COMMAND_LEN +
4032aedd662SScott Long 						    IPS_MAX_SG_LEN,
4042aedd662SScott Long 				/* flags     */	0,
405f6b1c44dSScott Long 				/* lockfunc  */ busdma_lock_mutex,
406f6b1c44dSScott Long 				/* lockarg   */ &Giant,
4072aedd662SScott Long 				&sc->command_dmatag) != 0) {
4082aedd662SScott Long                 device_printf(sc->dev, "can't alloc command dma tag\n");
4092aedd662SScott Long 		goto error;
4102aedd662SScott Long         }
4112aedd662SScott Long 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
4122aedd662SScott Long 				/* alignemnt */	1,
4132aedd662SScott Long 				/* boundary  */	0,
4142aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
4152aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
4162aedd662SScott Long 				/* filter    */	NULL,
4172aedd662SScott Long 				/* filterarg */	NULL,
4182aedd662SScott Long 				/* maxsize   */	IPS_MAX_IOBUF_SIZE,
4192aedd662SScott Long 				/* numsegs   */	IPS_MAX_SG_ELEMENTS,
4202aedd662SScott Long 				/* maxsegsize*/	IPS_MAX_IOBUF_SIZE,
4212aedd662SScott Long 				/* flags     */	0,
422f6b1c44dSScott Long 				/* lockfunc  */ busdma_lock_mutex,
423f6b1c44dSScott Long 				/* lockarg   */ &Giant,
4242aedd662SScott Long 				&sc->sg_dmatag) != 0) {
4252aedd662SScott Long 		device_printf(sc->dev, "can't alloc SG dma tag\n");
4262aedd662SScott Long 		goto error;
4272aedd662SScott Long 	}
4282aedd662SScott Long 	/* create one command buffer until we know how many commands this card
4292aedd662SScott Long            can handle */
4302aedd662SScott Long 	sc->max_cmds = 1;
4312aedd662SScott Long 	ips_cmdqueue_init(sc);
432dea4622dSScott Long 	callout_handle_init(&sc->timer);
4332aedd662SScott Long 
4342aedd662SScott Long 	if(sc->ips_adapter_reinit(sc, 0))
4352aedd662SScott Long 		goto error;
4362aedd662SScott Long 
4377633e7f1SMartin Blapp 	/* initialize ffdc values */
4387633e7f1SMartin Blapp 	microtime(&sc->ffdc_resettime);
4397633e7f1SMartin Blapp 	sc->ffdc_resetcount = 1;
4407633e7f1SMartin Blapp 	if ((i = ips_ffdc_reset(sc)) != 0) {
4417633e7f1SMartin Blapp 		device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i);
4427633e7f1SMartin Blapp 		goto error;
4437633e7f1SMartin Blapp 	}
444dea4622dSScott Long 	if ((i = ips_get_adapter_info(sc)) != 0) {
445dea4622dSScott Long 		device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i);
446dea4622dSScott Long 		goto error;
447dea4622dSScott Long 	}
4487633e7f1SMartin Blapp 	ips_update_nvram(sc); /* no error check as failure doesn't matter */
4497633e7f1SMartin Blapp 	if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){
4507633e7f1SMartin Blapp 		device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]);
4517633e7f1SMartin Blapp 	}
452dea4622dSScott Long  	if ((i = ips_get_drive_info(sc)) != 0) {
453dea4622dSScott Long 		device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i);
4542aedd662SScott Long 		goto error;
4552aedd662SScott Long 	}
4562aedd662SScott Long 
4572aedd662SScott Long         ips_cmdqueue_free(sc);
4582aedd662SScott Long 	if(sc->adapter_info.max_concurrent_cmds)
4592aedd662SScott Long         	sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds);
4602aedd662SScott Long 	else
4612aedd662SScott Long 		sc->max_cmds = 32;
4622aedd662SScott Long         if(ips_cmdqueue_init(sc)){
4632aedd662SScott Long 		device_printf(sc->dev, "failed to initialize command buffers\n");
4642aedd662SScott Long 		goto error;
4652aedd662SScott Long 	}
4662aedd662SScott Long         sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR,
4672aedd662SScott Long                                         S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev));
4682aedd662SScott Long 	sc->device_file->si_drv1 = sc;
4692aedd662SScott Long 	ips_diskdev_init(sc);
4702aedd662SScott Long 	sc->timer = timeout(ips_timeout, sc, 10*hz);
4712aedd662SScott Long         return 0;
4722aedd662SScott Long 
4732aedd662SScott Long error:
4742aedd662SScott Long 	ips_adapter_free(sc);
4752aedd662SScott Long 	return ENXIO;
4762aedd662SScott Long }
4772aedd662SScott Long 
4782aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization */
4792aedd662SScott Long int ips_morpheus_reinit(ips_softc_t *sc, int force)
4802aedd662SScott Long {
4812aedd662SScott Long         u_int32_t tmp;
4822aedd662SScott Long 	int i;
4832aedd662SScott Long 
4842aedd662SScott Long 	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4852aedd662SScott Long 	if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) &&
4862aedd662SScott Long 	    (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){
4872aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
4882aedd662SScott Long 		return 0;
4892aedd662SScott Long 	}
4902aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff);
4912aedd662SScott Long 	ips_read_4(sc, MORPHEUS_REG_OIMR);
4922aedd662SScott Long 
4932aedd662SScott Long 	device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n");
4942aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000);
4952aedd662SScott Long 	DELAY(5000000);
4962aedd662SScott Long 	pci_read_config(sc->dev, 0, 4);
4972aedd662SScott Long 
4982aedd662SScott Long 	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
4992aedd662SScott Long 	for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){
5002aedd662SScott Long 		DELAY(1000000);
5012aedd662SScott Long 		DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i);
5022aedd662SScott Long 		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
5032aedd662SScott Long 	}
5042aedd662SScott Long 	if(tmp & MORPHEUS_BIT_POST1)
5052aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1);
5062aedd662SScott Long 
5072aedd662SScott Long         if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){
5082aedd662SScott Long                 device_printf(sc->dev,"Adapter error during initialization.\n");
5092aedd662SScott Long 		return 1;
5102aedd662SScott Long         }
5112aedd662SScott Long 	for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){
5122aedd662SScott Long 		DELAY(1000000);
5132aedd662SScott Long 		DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i);
5142aedd662SScott Long 		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
5152aedd662SScott Long 	}
5162aedd662SScott Long 	if(tmp & MORPHEUS_BIT_POST2)
5172aedd662SScott Long 		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2);
5182aedd662SScott Long 
5192aedd662SScott Long 	if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){
5202aedd662SScott Long 		device_printf(sc->dev, "adapter failed config check\n");
5212aedd662SScott Long 		return 1;
5222aedd662SScott Long         }
5232aedd662SScott Long 	ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
5242aedd662SScott Long 	if(force && ips_clear_adapter(sc)){
5252aedd662SScott Long 		device_printf(sc->dev, "adapter clear failed\n");
5262aedd662SScott Long 		return 1;
5272aedd662SScott Long 	}
5282aedd662SScott Long 	return 0;
5292aedd662SScott Long }
5302aedd662SScott Long 
5312aedd662SScott Long /* clean up so we can unload the driver. */
5322aedd662SScott Long int ips_adapter_free(ips_softc_t *sc)
5332aedd662SScott Long {
5342aedd662SScott Long 	int error = 0;
5352aedd662SScott Long 	intrmask_t mask;
5362aedd662SScott Long 	if(sc->state & IPS_DEV_OPEN)
5372aedd662SScott Long 		return EBUSY;
5382aedd662SScott Long 	if((error = ips_diskdev_free(sc)))
5392aedd662SScott Long 		return error;
5402aedd662SScott Long 	if(ips_cmdqueue_free(sc)){
5412aedd662SScott Long 		device_printf(sc->dev,
5422aedd662SScott Long 		     "trying to exit when command queue is not empty!\n");
5432aedd662SScott Long 		return EBUSY;
5442aedd662SScott Long 	}
5452aedd662SScott Long 	DEVICE_PRINTF(1, sc->dev, "free\n");
5462aedd662SScott Long 	mask = splbio();
5472aedd662SScott Long 	untimeout(ips_timeout, sc, sc->timer);
5482aedd662SScott Long 	splx(mask);
5492aedd662SScott Long 
5502aedd662SScott Long 	if(sc->sg_dmatag)
5512aedd662SScott Long 		bus_dma_tag_destroy(sc->sg_dmatag);
5522aedd662SScott Long 	if(sc->command_dmatag)
5532aedd662SScott Long 		bus_dma_tag_destroy(sc->command_dmatag);
5542aedd662SScott Long 	if(sc->device_file)
5552aedd662SScott Long 	        destroy_dev(sc->device_file);
5562aedd662SScott Long         return 0;
5572aedd662SScott Long }
5582aedd662SScott Long 
5592aedd662SScott Long void ips_morpheus_intr(void *void_sc)
5602aedd662SScott Long {
5612aedd662SScott Long         ips_softc_t *sc = (ips_softc_t *)void_sc;
5622aedd662SScott Long 	u_int32_t oisr, iisr;
5632aedd662SScott Long 	int cmdnumber;
5642aedd662SScott Long 	ips_cmd_status_t status;
5652aedd662SScott Long 
5662aedd662SScott Long 	iisr =ips_read_4(sc, MORPHEUS_REG_IISR);
5672aedd662SScott Long 	oisr =ips_read_4(sc, MORPHEUS_REG_OISR);
5682aedd662SScott Long         PRINTF(9,"interrupt registers in:%x out:%x\n",iisr, oisr);
5692aedd662SScott Long 	if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){
5702aedd662SScott Long 		DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n");
5712aedd662SScott Long 		return;
5722aedd662SScott Long 	}
5732aedd662SScott Long 	while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){
5742aedd662SScott Long 		cmdnumber = status.fields.command_id;
5752aedd662SScott Long 		sc->commandarray[cmdnumber].status.value = status.value;
5762aedd662SScott Long 		sc->commandarray[cmdnumber].timeout = 0;
5772aedd662SScott Long 		sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
5782aedd662SScott Long 
5792aedd662SScott Long 
5802aedd662SScott Long 		DEVICE_PRINTF(9,sc->dev, "got command %d\n", cmdnumber);
5812aedd662SScott Long 	}
5822aedd662SScott Long         return;
5832aedd662SScott Long }
5842aedd662SScott Long 
5852aedd662SScott Long void ips_issue_morpheus_cmd(ips_command_t *command)
5862aedd662SScott Long {
5872aedd662SScott Long 	intrmask_t mask = splbio();
5882aedd662SScott Long 	/* hmmm, is there a cleaner way to do this? */
5892aedd662SScott Long 	if(command->sc->state & IPS_OFFLINE){
5902aedd662SScott Long 		splx(mask);
5912aedd662SScott Long 		command->status.value = IPS_ERROR_STATUS;
5922aedd662SScott Long 		command->callback(command);
5932aedd662SScott Long 		return;
5942aedd662SScott Long 	}
5952aedd662SScott Long 	command->timeout = 10;
5962aedd662SScott Long 	ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr);
5972aedd662SScott Long 	splx(mask);
5982aedd662SScott Long }
5992aedd662SScott Long 
6002aedd662SScott Long static void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error)
6012aedd662SScott Long {
6022aedd662SScott Long 	ips_copper_queue_t *queue = queueptr;
6032aedd662SScott Long 	if(error){
6042aedd662SScott Long 		return;
6052aedd662SScott Long 	}
6062aedd662SScott Long 	queue->base_phys_addr = segments[0].ds_addr;
6072aedd662SScott Long }
6082aedd662SScott Long 
6092aedd662SScott Long static int ips_copperhead_queue_init(ips_softc_t *sc)
6102aedd662SScott Long {
6112aedd662SScott Long 	int error;
6122aedd662SScott Long 	bus_dma_tag_t dmatag;
6132aedd662SScott Long 	bus_dmamap_t dmamap;
6142aedd662SScott Long        	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
6152aedd662SScott Long 				/* alignemnt */	1,
6162aedd662SScott Long 				/* boundary  */	0,
6172aedd662SScott Long 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
6182aedd662SScott Long 				/* highaddr  */	BUS_SPACE_MAXADDR,
6192aedd662SScott Long 				/* filter    */	NULL,
6202aedd662SScott Long 				/* filterarg */	NULL,
6212aedd662SScott Long 				/* maxsize   */	sizeof(ips_copper_queue_t),
6222aedd662SScott Long 				/* numsegs   */	1,
6232aedd662SScott Long 				/* maxsegsize*/	sizeof(ips_copper_queue_t),
6242aedd662SScott Long 				/* flags     */	0,
625f6b1c44dSScott Long 				/* lockfunc  */ busdma_lock_mutex,
626f6b1c44dSScott Long 				/* lockarg   */ &Giant,
6272aedd662SScott Long 				&dmatag) != 0) {
6282aedd662SScott Long                 device_printf(sc->dev, "can't alloc dma tag for statue queue\n");
6292aedd662SScott Long 		error = ENOMEM;
6302aedd662SScott Long 		goto exit;
6312aedd662SScott Long         }
6322aedd662SScott Long 	if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue),
6332aedd662SScott Long 	   		    BUS_DMA_NOWAIT, &dmamap)){
6342aedd662SScott Long 		error = ENOMEM;
6352aedd662SScott Long 		goto exit;
6362aedd662SScott Long 	}
6372aedd662SScott Long 	bzero(sc->copper_queue, sizeof(ips_copper_queue_t));
6382aedd662SScott Long 	sc->copper_queue->dmatag = dmatag;
6392aedd662SScott Long 	sc->copper_queue->dmamap = dmamap;
6402aedd662SScott Long 	sc->copper_queue->nextstatus = 1;
6412aedd662SScott Long 	bus_dmamap_load(dmatag, dmamap,
6422aedd662SScott Long 			&(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4,
6432aedd662SScott Long 			ips_copperhead_queue_callback, sc->copper_queue,
6442aedd662SScott Long 			BUS_DMA_NOWAIT);
6452aedd662SScott Long 	if(sc->copper_queue->base_phys_addr == 0){
6462aedd662SScott Long 		error = ENOMEM;
6472aedd662SScott Long 		goto exit;
6482aedd662SScott Long 	}
6492aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr);
6502aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr +
6512aedd662SScott Long 		    IPS_MAX_CMD_NUM * 4);
6522aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4);
6532aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr);
6542aedd662SScott Long 
6552aedd662SScott Long 
6562aedd662SScott Long 	return 0;
6572aedd662SScott Long exit:
6582aedd662SScott Long 	bus_dmamem_free(dmatag, sc->copper_queue, dmamap);
6592aedd662SScott Long 	bus_dma_tag_destroy(dmatag);
6602aedd662SScott Long 	return error;
6612aedd662SScott Long }
6622aedd662SScott Long 
6632aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */
6642aedd662SScott Long int ips_copperhead_reinit(ips_softc_t *sc, int force)
6652aedd662SScott Long {
6662aedd662SScott Long 	int i, j;
6672aedd662SScott Long 	u_int32_t postcode = 0, configstatus = 0;
6682aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, 0x80);
6692aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, 0);
6702aedd662SScott Long 	device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n");
6712aedd662SScott Long 	for(j = 0; j < 2; j++){
6722aedd662SScott Long 		postcode <<= 8;
6732aedd662SScott Long 		for(i = 0; i < 45; i++){
6742aedd662SScott Long 			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
6752aedd662SScott Long 				postcode |= ips_read_1(sc, COPPER_REG_ISPR);
6762aedd662SScott Long 				ips_write_1(sc, COPPER_REG_HISR,
6772aedd662SScott Long 					    COPPER_GHI_BIT);
6782aedd662SScott Long 				break;
6792aedd662SScott Long 			} else
6802aedd662SScott Long 				DELAY(1000000);
6812aedd662SScott Long 		}
6822aedd662SScott Long 		if(i == 45)
6832aedd662SScott Long 			return 1;
6842aedd662SScott Long 	}
6852aedd662SScott Long 	for(j = 0; j < 2; j++){
6862aedd662SScott Long 		configstatus <<= 8;
6872aedd662SScott Long 		for(i = 0; i < 240; i++){
6882aedd662SScott Long 			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
6892aedd662SScott Long 				configstatus |= ips_read_1(sc, COPPER_REG_ISPR);
6902aedd662SScott Long 				ips_write_1(sc, COPPER_REG_HISR,
6912aedd662SScott Long 					    COPPER_GHI_BIT);
6922aedd662SScott Long 				break;
6932aedd662SScott Long 			} else
6942aedd662SScott Long 				DELAY(1000000);
6952aedd662SScott Long 		}
6962aedd662SScott Long 		if(i == 240)
6972aedd662SScott Long 			return 1;
6982aedd662SScott Long 	}
6992aedd662SScott Long 	for(i = 0; i < 240; i++){
7002aedd662SScott Long 		if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){
7012aedd662SScott Long 			break;
7022aedd662SScott Long 		} else
7032aedd662SScott Long 			DELAY(1000000);
7042aedd662SScott Long 	}
7052aedd662SScott Long 	if(i == 240)
7062aedd662SScott Long 		return 1;
7072aedd662SScott Long 	ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT);
7082aedd662SScott Long 	ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT);
7092aedd662SScott Long 	ips_copperhead_queue_init(sc);
7102aedd662SScott Long 	ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT);
7112aedd662SScott Long 	i = ips_read_1(sc, COPPER_REG_SCPR);
7122aedd662SScott Long 	ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT);
7132aedd662SScott Long 	if(!configstatus){
7142aedd662SScott Long 		device_printf(sc->dev, "adapter initialization failed\n");
7152aedd662SScott Long 		return 1;
7162aedd662SScott Long 	}
7172aedd662SScott Long 	if(force && ips_clear_adapter(sc)){
7182aedd662SScott Long 		device_printf(sc->dev, "adapter clear failed\n");
7192aedd662SScott Long 		return 1;
7202aedd662SScott Long 	}
7212aedd662SScott Long 	return 0;
7222aedd662SScott Long }
7232aedd662SScott Long static u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc)
7242aedd662SScott Long {
7252aedd662SScott Long 	intrmask_t mask;
7262aedd662SScott Long 	u_int32_t value;
7272aedd662SScott Long 	int statnum = sc->copper_queue->nextstatus++;
7282aedd662SScott Long 	if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM)
7292aedd662SScott Long 		sc->copper_queue->nextstatus = 0;
7302aedd662SScott Long 	mask = splbio();
7312aedd662SScott Long 	value = sc->copper_queue->status[statnum];
7322aedd662SScott Long 	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr +
7332aedd662SScott Long 		    4 * statnum);
7342aedd662SScott Long 	splx(mask);
7352aedd662SScott Long 	return value;
7362aedd662SScott Long }
7372aedd662SScott Long 
7382aedd662SScott Long 
7392aedd662SScott Long void ips_copperhead_intr(void *void_sc)
7402aedd662SScott Long {
7412aedd662SScott Long         ips_softc_t *sc = (ips_softc_t *)void_sc;
7422aedd662SScott Long 	int cmdnumber;
7432aedd662SScott Long 	ips_cmd_status_t status;
7442aedd662SScott Long 
7452aedd662SScott Long 	while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){
7462aedd662SScott Long 		status.value = ips_copperhead_cmd_status(sc);
7472aedd662SScott Long 		cmdnumber = status.fields.command_id;
7482aedd662SScott Long 		sc->commandarray[cmdnumber].status.value = status.value;
7492aedd662SScott Long 		sc->commandarray[cmdnumber].timeout = 0;
7502aedd662SScott Long 		sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
7512aedd662SScott Long 		PRINTF(9, "ips: got command %d\n", cmdnumber);
7522aedd662SScott Long 	}
7532aedd662SScott Long         return;
7542aedd662SScott Long }
7552aedd662SScott Long 
7562aedd662SScott Long void ips_issue_copperhead_cmd(ips_command_t *command)
7572aedd662SScott Long {
7582aedd662SScott Long 	int i;
7592aedd662SScott Long 	intrmask_t mask = splbio();
7602aedd662SScott Long 	/* hmmm, is there a cleaner way to do this? */
7612aedd662SScott Long 	if(command->sc->state & IPS_OFFLINE){
7622aedd662SScott Long 		splx(mask);
7632aedd662SScott Long 		command->status.value = IPS_ERROR_STATUS;
7642aedd662SScott Long 		command->callback(command);
7652aedd662SScott Long 		return;
7662aedd662SScott Long 	}
7672aedd662SScott Long 	command->timeout = 10;
7682aedd662SScott Long 	for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT;
7692aedd662SScott Long 	    i++ ){
7702aedd662SScott Long 		if( i == 20){
7712aedd662SScott Long printf("sem bit still set, can't send a command\n");
7722aedd662SScott Long 			splx(mask);
7732aedd662SScott Long 			return;
7742aedd662SScott Long 		}
7752aedd662SScott Long 		DELAY(500);/* need to do a delay here */
7762aedd662SScott Long 	}
7772aedd662SScott Long 	ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr);
7782aedd662SScott Long 	ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START);
7792aedd662SScott Long 	splx(mask);
7802aedd662SScott Long }
7812aedd662SScott Long 
782