1114902Sscottl/*-
2119418Sobrien * Written by: David Jeffery
3114902Sscottl * Copyright (c) 2002 Adaptec Inc.
4114902Sscottl * All rights reserved.
5114902Sscottl *
6114902Sscottl * Redistribution and use in source and binary forms, with or without
7114902Sscottl * modification, are permitted provided that the following conditions
8114902Sscottl * are met:
9114902Sscottl * 1. Redistributions of source code must retain the above copyright
10114902Sscottl *    notice, this list of conditions and the following disclaimer.
11114902Sscottl * 2. Redistributions in binary form must reproduce the above copyright
12114902Sscottl *    notice, this list of conditions and the following disclaimer in the
13114902Sscottl *    documentation and/or other materials provided with the distribution.
14114902Sscottl *
15114902Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16114902Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17114902Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18114902Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19114902Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20114902Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21114902Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22114902Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23114902Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24114902Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25114902Sscottl * SUCH DAMAGE.
26114902Sscottl */
27114902Sscottl
28119418Sobrien#include <sys/cdefs.h>
29119418Sobrien__FBSDID("$FreeBSD$");
30114902Sscottl
31152919Sscottl#include <dev/ips/ipsreg.h>
32114902Sscottl#include <dev/ips/ips.h>
33114902Sscottl#include <sys/stat.h>
34114902Sscottl#include <sys/time.h>
35114902Sscottl
36114902Sscottlstatic d_open_t ips_open;
37114902Sscottlstatic d_close_t ips_close;
38114902Sscottlstatic d_ioctl_t ips_ioctl;
39114902Sscottl
40129859SscottlMALLOC_DEFINE(M_IPSBUF, "ipsbuf","IPS driver buffer");
41129859Sscottl
42114902Sscottlstatic struct cdevsw ips_cdevsw = {
43126080Sphk	.d_version =	D_VERSION,
44126080Sphk	.d_flags =	D_NEEDGIANT,
45125808Sphk	.d_open =	ips_open,
46125808Sphk	.d_close =	ips_close,
47125808Sphk	.d_ioctl =	ips_ioctl,
48125808Sphk	.d_name =	"ips",
49114902Sscottl};
50114902Sscottl
51122999Smbrstatic const char* ips_adapter_name[] = {
52122999Smbr	"N/A",
53122999Smbr	"ServeRAID (copperhead)",
54122999Smbr	"ServeRAID II (copperhead refresh)",
55122999Smbr	"ServeRAID onboard (copperhead)",
56122999Smbr	"ServeRAID onboard (copperhead)",
57122999Smbr	"ServeRAID 3H (clarinet)",
58122999Smbr	"ServeRAID 3L (clarinet lite)",
59122999Smbr	"ServeRAID 4H (trombone)",
60122999Smbr	"ServeRAID 4M (morpheus)",
61122999Smbr	"ServeRAID 4L (morpheus lite)",
62122999Smbr	"ServeRAID 4Mx (neo)",
63122999Smbr	"ServeRAID 4Lx (neo lite)",
64122999Smbr	"ServeRAID 5i II (sarasota)",
65122999Smbr	"ServeRAID 5i (sarasota)",
66122999Smbr	"ServeRAID 6M (marco)",
67163024Smaxim	"ServeRAID 6i (sebring)",
68163024Smaxim	"ServeRAID 7t",
69163024Smaxim	"ServeRAID 7k",
70163024Smaxim	"ServeRAID 7M"
71122999Smbr};
72114902Sscottl
73122999Smbr
74130585Sphkstatic int ips_open(struct cdev *dev, int flags, int fmt, struct thread *td)
75114902Sscottl{
76114902Sscottl	ips_softc_t *sc = dev->si_drv1;
77114902Sscottl	sc->state |= IPS_DEV_OPEN;
78114902Sscottl        return 0;
79114902Sscottl}
80114902Sscottl
81130585Sphkstatic int ips_close(struct cdev *dev, int flags, int fmt, struct thread *td)
82114902Sscottl{
83114902Sscottl	ips_softc_t *sc = dev->si_drv1;
84114902Sscottl	sc->state &= ~IPS_DEV_OPEN;
85114902Sscottl
86114902Sscottl        return 0;
87114902Sscottl}
88114902Sscottl
89130585Sphkstatic int ips_ioctl(struct cdev *dev, u_long command, caddr_t addr, int32_t flags, struct thread *td)
90114902Sscottl{
91114902Sscottl	ips_softc_t *sc;
92114902Sscottl
93114902Sscottl	sc = dev->si_drv1;
94114902Sscottl	return ips_ioctl_request(sc, command, addr, flags);
95114902Sscottl}
96114902Sscottl
97114902Sscottlstatic void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error)
98114902Sscottl{
99114902Sscottl	ips_command_t *command = cmdptr;
100114902Sscottl	PRINTF(10, "ips: in ips_cmd_dmaload\n");
101114902Sscottl	if(!error)
102114902Sscottl		command->command_phys_addr = segments[0].ds_addr;
103114902Sscottl
104114902Sscottl}
105114902Sscottl
106114902Sscottl/* is locking needed? what locking guarentees are there on removal? */
107140923Sscottlstatic int ips_cmdqueue_free(ips_softc_t *sc)
108114902Sscottl{
109114902Sscottl	int i, error = -1;
110126364Sscottl	ips_command_t *command;
111126364Sscottl
112114902Sscottl	if(!sc->used_commands){
113114902Sscottl		for(i = 0; i < sc->max_cmds; i++){
114126364Sscottl
115126364Sscottl			command = &sc->commandarray[i];
116126364Sscottl
117126364Sscottl			if(command->command_phys_addr == 0)
118114902Sscottl				continue;
119114902Sscottl			bus_dmamap_unload(sc->command_dmatag,
120126364Sscottl					  command->command_dmamap);
121114902Sscottl			bus_dmamem_free(sc->command_dmatag,
122126364Sscottl					command->command_buffer,
123126364Sscottl					command->command_dmamap);
124140923Sscottl			if (command->data_dmamap != NULL)
125140923Sscottl				bus_dmamap_destroy(command->data_dmatag,
126140923Sscottl				    command->data_dmamap);
127114902Sscottl		}
128114902Sscottl		error = 0;
129114902Sscottl		sc->state |= IPS_OFFLINE;
130114902Sscottl	}
131140923Sscottl	sc->staticcmd = NULL;
132140923Sscottl	free(sc->commandarray, M_DEVBUF);
133114902Sscottl	return error;
134114902Sscottl}
135114902Sscottl
136114902Sscottl/* places all ips command structs on the free command queue.  No locking as if someone else tries
137114902Sscottl * to access this during init, we have bigger problems */
138140923Sscottlstatic int ips_cmdqueue_init(ips_softc_t *sc)
139114902Sscottl{
140114902Sscottl	int i;
141114902Sscottl	ips_command_t *command;
142140923Sscottl
143140923Sscottl	sc->commandarray = (ips_command_t *)malloc(sizeof(ips_command_t) *
144140923Sscottl	    sc->max_cmds, M_DEVBUF, M_NOWAIT|M_ZERO);
145140923Sscottl	if (sc->commandarray == NULL)
146140923Sscottl		return (ENOMEM);
147140923Sscottl
148114902Sscottl	SLIST_INIT(&sc->free_cmd_list);
149114902Sscottl	for(i = 0; i < sc->max_cmds; i++){
150114902Sscottl		command = &sc->commandarray[i];
151126364Sscottl		command->id = i;
152126364Sscottl		command->sc = sc;
153126364Sscottl
154114902Sscottl		if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer,
155114902Sscottl		    BUS_DMA_NOWAIT, &command->command_dmamap))
156114902Sscottl			goto error;
157114902Sscottl		bus_dmamap_load(sc->command_dmatag, command->command_dmamap,
158114902Sscottl				command->command_buffer,IPS_COMMAND_LEN,
159114902Sscottl				ips_cmd_dmaload, command, BUS_DMA_NOWAIT);
160114902Sscottl		if(!command->command_phys_addr){
161114902Sscottl			bus_dmamem_free(sc->command_dmatag,
162114902Sscottl			    command->command_buffer, command->command_dmamap);
163114902Sscottl			goto error;
164114902Sscottl		}
165126364Sscottl
166140923Sscottl		if (i != 0) {
167140923Sscottl			command->data_dmatag = sc->sg_dmatag;
168140923Sscottl			if (bus_dmamap_create(command->data_dmatag, 0,
169140923Sscottl			    &command->data_dmamap))
170140923Sscottl				goto error;
171140923Sscottl			SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
172140923Sscottl		} else
173140923Sscottl			sc->staticcmd = command;
174114902Sscottl	}
175114902Sscottl	sc->state &= ~IPS_OFFLINE;
176114902Sscottl	return 0;
177114902Sscottlerror:
178126364Sscottl	ips_cmdqueue_free(sc);
179126364Sscottl	return ENOMEM;
180114902Sscottl}
181114902Sscottl
182114902Sscottl/* returns a free command struct if one is available.
183114902Sscottl * It also blanks out anything that may be a wild pointer/value.
184114902Sscottl * Also, command buffers are not freed.  They are
185114902Sscottl * small so they are saved and kept dmamapped and loaded.
186114902Sscottl */
187140923Sscottlint ips_get_free_cmd(ips_softc_t *sc, ips_command_t **cmd, unsigned long flags)
188114902Sscottl{
189114902Sscottl	ips_command_t *command;
190114902Sscottl
191114902Sscottl	if(sc->state & IPS_OFFLINE){
192114902Sscottl		return EIO;
193114902Sscottl	}
194140923Sscottl	if ((flags & IPS_STATIC_FLAG) == 0) {
195140923Sscottl		command = SLIST_FIRST(&sc->free_cmd_list);
196140923Sscottl		if(!command || (sc->state & IPS_TIMEOUT)){
197140923Sscottl			return EBUSY;
198140923Sscottl		}
199140923Sscottl		SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
200140923Sscottl		(sc->used_commands)++;
201140923Sscottl	} else {
202140923Sscottl		if (sc->state & IPS_STATIC_BUSY)
203114902Sscottl			return EAGAIN;
204140923Sscottl		command = sc->staticcmd;
205140923Sscottl		sc->state |= IPS_STATIC_BUSY;
206114902Sscottl	}
207114902Sscottl	clear_ips_command(command);
208114902Sscottl	bzero(command->command_buffer, IPS_COMMAND_LEN);
209140923Sscottl	*cmd = command;
210140923Sscottl	return 0;
211114902Sscottl}
212114902Sscottl
213114902Sscottl/* adds a command back to the free command queue */
214114902Sscottlvoid ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command)
215114902Sscottl{
216126364Sscottl
217140923Sscottl	if (sema_value(&sc->cmd_sema) != 0)
218126364Sscottl		panic("ips: command returned non-zero semaphore");
219126364Sscottl
220140923Sscottl	if (command != sc->staticcmd) {
221140923Sscottl		SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
222140923Sscottl		(sc->used_commands)--;
223140923Sscottl	} else {
224140923Sscottl		sc->state &= ~IPS_STATIC_BUSY;
225140923Sscottl	}
226114902Sscottl}
227122999Smbrstatic const char* ips_diskdev_statename(u_int8_t state)
228122999Smbr{
229122999Smbr	static char statebuf[20];
230122999Smbr	switch(state){
231122999Smbr		case IPS_LD_OFFLINE:
232122999Smbr			return("OFFLINE");
233122999Smbr			break;
234122999Smbr		case IPS_LD_OKAY:
235122999Smbr			return("OK");
236122999Smbr			break;
237122999Smbr		case IPS_LD_DEGRADED:
238122999Smbr			return("DEGRADED");
239122999Smbr			break;
240122999Smbr		case IPS_LD_FREE:
241122999Smbr			return("FREE");
242122999Smbr			break;
243122999Smbr		case IPS_LD_SYS:
244122999Smbr			return("SYS");
245122999Smbr			break;
246122999Smbr		case IPS_LD_CRS:
247122999Smbr			return("CRS");
248122999Smbr			break;
249122999Smbr	}
250122999Smbr	sprintf(statebuf,"UNKNOWN(0x%02x)", state);
251122999Smbr	return(statebuf);
252122999Smbr}
253114902Sscottl
254114902Sscottlstatic int ips_diskdev_init(ips_softc_t *sc)
255114902Sscottl{
256114902Sscottl	int i;
257114902Sscottl	for(i=0; i < IPS_MAX_NUM_DRIVES; i++){
258122999Smbr		if(sc->drives[i].state == IPS_LD_FREE) continue;
259122999Smbr		device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n",
260122999Smbr			i, sc->drives[i].raid_lvl,
261122999Smbr			sc->drives[i].sector_count,
262122999Smbr			ips_diskdev_statename(sc->drives[i].state));
263122999Smbr		if(sc->drives[i].state == IPS_LD_OKAY ||
264122999Smbr		   sc->drives[i].state == IPS_LD_DEGRADED){
265114902Sscottl			sc->diskdev[i] = device_add_child(sc->dev, NULL, -1);
266116931Speter			device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i);
267114902Sscottl		}
268114902Sscottl	}
269114902Sscottl	if(bus_generic_attach(sc->dev)){
270114902Sscottl		device_printf(sc->dev, "Attaching bus failed\n");
271114902Sscottl	}
272114902Sscottl	return 0;
273114902Sscottl}
274114902Sscottl
275114902Sscottlstatic int ips_diskdev_free(ips_softc_t *sc)
276114902Sscottl{
277114902Sscottl	int i;
278114902Sscottl	int error = 0;
279114902Sscottl	for(i = 0; i < IPS_MAX_NUM_DRIVES; i++){
280114902Sscottl		if(sc->diskdev[i])
281114902Sscottl			error = device_delete_child(sc->dev, sc->diskdev[i]);
282114902Sscottl			if(error)
283114902Sscottl				return error;
284114902Sscottl	}
285114902Sscottl	bus_generic_detach(sc->dev);
286114902Sscottl	return 0;
287114902Sscottl}
288114902Sscottl
289114902Sscottl/* ips_timeout is periodically called to make sure no commands sent
290114902Sscottl * to the card have become stuck.  If it finds a stuck command, it
291114902Sscottl * sets a flag so the driver won't start any more commands and then
292114902Sscottl * is periodically called to see if all outstanding commands have
293114902Sscottl * either finished or timed out.  Once timed out, an attempt to
294114902Sscottl * reinitialize the card is made.  If that fails, the driver gives
295114902Sscottl * up and declares the card dead. */
296114902Sscottlstatic void ips_timeout(void *arg)
297114902Sscottl{
298114902Sscottl	ips_softc_t *sc = arg;
299114902Sscottl	int i, state = 0;
300114902Sscottl	ips_command_t *command;
301140923Sscottl
302140923Sscottl	mtx_lock(&sc->queue_mtx);
303114902Sscottl	command = &sc->commandarray[0];
304114902Sscottl	for(i = 0; i < sc->max_cmds; i++){
305114902Sscottl		if(!command[i].timeout){
306114902Sscottl			continue;
307114902Sscottl		}
308114902Sscottl		command[i].timeout--;
309114902Sscottl		if(!command[i].timeout){
310114902Sscottl			if(!(sc->state & IPS_TIMEOUT)){
311114902Sscottl				sc->state |= IPS_TIMEOUT;
312114902Sscottl				device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n");
313114902Sscottl			}
314150535Sscottl			ips_set_error(&command[i], ETIMEDOUT);
315114902Sscottl			command[i].callback(&command[i]);
316114902Sscottl			/* hmm, this should be enough cleanup */
317114902Sscottl		} else
318114902Sscottl			state = 1;
319114902Sscottl	}
320114902Sscottl	if(!state && (sc->state & IPS_TIMEOUT)){
321114902Sscottl		if(sc->ips_adapter_reinit(sc, 1)){
322114902Sscottl			device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n");
323114902Sscottl			sc->state |= IPS_OFFLINE;
324114902Sscottl			sc->state &= ~IPS_TIMEOUT;
325114902Sscottl			/* Grr, I hate this solution. I run waiting commands
326114902Sscottl			   one at a time and error them out just before they
327114902Sscottl			   would go to the card. This sucks. */
328114902Sscottl		} else
329114902Sscottl			sc->state &= ~IPS_TIMEOUT;
330114902Sscottl	}
331114902Sscottl	if (sc->state != IPS_OFFLINE)
332114902Sscottl		sc->timer = timeout(ips_timeout, sc, 10*hz);
333140923Sscottl	mtx_unlock(&sc->queue_mtx);
334114902Sscottl}
335114902Sscottl
336114902Sscottl/* check card and initialize it */
337114902Sscottlint ips_adapter_init(ips_softc_t *sc)
338114902Sscottl{
339116852Sscottl        int i;
340114902Sscottl        DEVICE_PRINTF(1,sc->dev, "initializing\n");
341126364Sscottl
342114902Sscottl        if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
343114902Sscottl				/* alignemnt */	1,
344114902Sscottl				/* boundary  */	0,
345114902Sscottl				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
346114902Sscottl				/* highaddr  */	BUS_SPACE_MAXADDR,
347114902Sscottl				/* filter    */	NULL,
348114902Sscottl				/* filterarg */	NULL,
349114902Sscottl				/* maxsize   */	IPS_COMMAND_LEN +
350114902Sscottl						    IPS_MAX_SG_LEN,
351114902Sscottl				/* numsegs   */	1,
352114902Sscottl				/* maxsegsize*/	IPS_COMMAND_LEN +
353114902Sscottl						    IPS_MAX_SG_LEN,
354114902Sscottl				/* flags     */	0,
355140923Sscottl				/* lockfunc  */ NULL,
356140923Sscottl				/* lockarg   */ NULL,
357114902Sscottl				&sc->command_dmatag) != 0) {
358114902Sscottl                device_printf(sc->dev, "can't alloc command dma tag\n");
359114902Sscottl		goto error;
360114902Sscottl        }
361114902Sscottl	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
362114902Sscottl				/* alignemnt */	1,
363114902Sscottl				/* boundary  */	0,
364114902Sscottl				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
365114902Sscottl				/* highaddr  */	BUS_SPACE_MAXADDR,
366114902Sscottl				/* filter    */	NULL,
367114902Sscottl				/* filterarg */	NULL,
368114902Sscottl				/* maxsize   */	IPS_MAX_IOBUF_SIZE,
369114902Sscottl				/* numsegs   */	IPS_MAX_SG_ELEMENTS,
370114902Sscottl				/* maxsegsize*/	IPS_MAX_IOBUF_SIZE,
371114902Sscottl				/* flags     */	0,
372117126Sscottl				/* lockfunc  */ busdma_lock_mutex,
373140923Sscottl				/* lockarg   */ &sc->queue_mtx,
374114902Sscottl				&sc->sg_dmatag) != 0) {
375114902Sscottl		device_printf(sc->dev, "can't alloc SG dma tag\n");
376114902Sscottl		goto error;
377114902Sscottl	}
378114902Sscottl	/* create one command buffer until we know how many commands this card
379114902Sscottl           can handle */
380114902Sscottl	sc->max_cmds = 1;
381114902Sscottl	ips_cmdqueue_init(sc);
382116852Sscottl	callout_handle_init(&sc->timer);
383114902Sscottl
384114902Sscottl	if(sc->ips_adapter_reinit(sc, 0))
385114902Sscottl		goto error;
386114902Sscottl
387122999Smbr	/* initialize ffdc values */
388122999Smbr	microtime(&sc->ffdc_resettime);
389122999Smbr	sc->ffdc_resetcount = 1;
390122999Smbr	if ((i = ips_ffdc_reset(sc)) != 0) {
391122999Smbr		device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i);
392122999Smbr		goto error;
393122999Smbr	}
394116852Sscottl	if ((i = ips_get_adapter_info(sc)) != 0) {
395116852Sscottl		device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i);
396114902Sscottl		goto error;
397114902Sscottl	}
398122999Smbr	ips_update_nvram(sc); /* no error check as failure doesn't matter */
399122999Smbr	if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){
400122999Smbr		device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]);
401122999Smbr	}
402116852Sscottl 	if ((i = ips_get_drive_info(sc)) != 0) {
403116852Sscottl		device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i);
404116852Sscottl		goto error;
405116852Sscottl	}
406114902Sscottl
407114902Sscottl        ips_cmdqueue_free(sc);
408114902Sscottl	if(sc->adapter_info.max_concurrent_cmds)
409114902Sscottl        	sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds);
410114902Sscottl	else
411114902Sscottl		sc->max_cmds = 32;
412114902Sscottl        if(ips_cmdqueue_init(sc)){
413114902Sscottl		device_printf(sc->dev, "failed to initialize command buffers\n");
414114902Sscottl		goto error;
415114902Sscottl	}
416114902Sscottl        sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR,
417114902Sscottl                                        S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev));
418114902Sscottl	sc->device_file->si_drv1 = sc;
419114902Sscottl	ips_diskdev_init(sc);
420114902Sscottl	sc->timer = timeout(ips_timeout, sc, 10*hz);
421114902Sscottl        return 0;
422114902Sscottl
423114902Sscottlerror:
424114902Sscottl	ips_adapter_free(sc);
425114902Sscottl	return ENXIO;
426114902Sscottl}
427114902Sscottl
428114902Sscottl/* see if we should reinitialize the card and wait for it to timeout or complete initialization */
429114902Sscottlint ips_morpheus_reinit(ips_softc_t *sc, int force)
430114902Sscottl{
431114902Sscottl        u_int32_t tmp;
432114902Sscottl	int i;
433114902Sscottl
434114902Sscottl	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
435114902Sscottl	if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) &&
436114902Sscottl	    (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){
437114902Sscottl		ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
438114902Sscottl		return 0;
439114902Sscottl	}
440114902Sscottl	ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff);
441114902Sscottl	ips_read_4(sc, MORPHEUS_REG_OIMR);
442114902Sscottl
443114902Sscottl	device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n");
444114902Sscottl	ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000);
445114902Sscottl	DELAY(5000000);
446152919Sscottl	ips_read_4(sc, MORPHEUS_REG_OIMR);
447114902Sscottl
448114902Sscottl	tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
449114902Sscottl	for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){
450114902Sscottl		DELAY(1000000);
451114902Sscottl		DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i);
452114902Sscottl		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
453114902Sscottl	}
454114902Sscottl	if(tmp & MORPHEUS_BIT_POST1)
455114902Sscottl		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1);
456114902Sscottl
457114902Sscottl        if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){
458114902Sscottl                device_printf(sc->dev,"Adapter error during initialization.\n");
459114902Sscottl		return 1;
460114902Sscottl        }
461114902Sscottl	for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){
462114902Sscottl		DELAY(1000000);
463114902Sscottl		DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i);
464114902Sscottl		tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
465114902Sscottl	}
466114902Sscottl	if(tmp & MORPHEUS_BIT_POST2)
467114902Sscottl		ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2);
468114902Sscottl
469114902Sscottl	if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){
470114902Sscottl		device_printf(sc->dev, "adapter failed config check\n");
471114902Sscottl		return 1;
472114902Sscottl        }
473114902Sscottl	ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
474114902Sscottl	if(force && ips_clear_adapter(sc)){
475114902Sscottl		device_printf(sc->dev, "adapter clear failed\n");
476114902Sscottl		return 1;
477114902Sscottl	}
478114902Sscottl	return 0;
479114902Sscottl}
480114902Sscottl
481114902Sscottl/* clean up so we can unload the driver. */
482114902Sscottlint ips_adapter_free(ips_softc_t *sc)
483114902Sscottl{
484114902Sscottl	int error = 0;
485114902Sscottl	if(sc->state & IPS_DEV_OPEN)
486114902Sscottl		return EBUSY;
487114902Sscottl	if((error = ips_diskdev_free(sc)))
488114902Sscottl		return error;
489114902Sscottl	if(ips_cmdqueue_free(sc)){
490114902Sscottl		device_printf(sc->dev,
491114902Sscottl		     "trying to exit when command queue is not empty!\n");
492114902Sscottl		return EBUSY;
493114902Sscottl	}
494114902Sscottl	DEVICE_PRINTF(1, sc->dev, "free\n");
495114902Sscottl	untimeout(ips_timeout, sc, sc->timer);
496114902Sscottl
497114902Sscottl	if(sc->sg_dmatag)
498114902Sscottl		bus_dma_tag_destroy(sc->sg_dmatag);
499114902Sscottl	if(sc->command_dmatag)
500114902Sscottl		bus_dma_tag_destroy(sc->command_dmatag);
501114902Sscottl	if(sc->device_file)
502114902Sscottl	        destroy_dev(sc->device_file);
503114902Sscottl        return 0;
504114902Sscottl}
505114902Sscottl
506141062Sscottlstatic __inline int ips_morpheus_check_intr(ips_softc_t *sc)
507114902Sscottl{
508114902Sscottl	int cmdnumber;
509114902Sscottl	ips_cmd_status_t status;
510141062Sscottl	ips_command_t *command;
511141062Sscottl	int found = 0;
512141062Sscottl	u_int32_t oisr;
513114902Sscottl
514141062Sscottl	oisr = ips_read_4(sc, MORPHEUS_REG_OISR);
515141062Sscottl	PRINTF(9, "interrupt registers out:%x\n", oisr);
516114902Sscottl	if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){
517114902Sscottl		DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n");
518141062Sscottl		return (0);
519114902Sscottl	}
520114902Sscottl	while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){
521114902Sscottl		cmdnumber = status.fields.command_id;
522141062Sscottl		command = &sc->commandarray[cmdnumber];
523141062Sscottl		command->status.value = status.value;
524141062Sscottl		command->timeout = 0;
525141062Sscottl		command->callback(command);
526114902Sscottl
527141062Sscottl		found = 1;
528114902Sscottl	}
529141062Sscottl        return (found);
530141062Sscottl}
531141062Sscottl
532141062Sscottlvoid ips_morpheus_intr(void *void_sc)
533141062Sscottl{
534141062Sscottl	ips_softc_t *sc = void_sc;
535141062Sscottl
536141062Sscottl	mtx_lock(&sc->queue_mtx);
537141062Sscottl	ips_morpheus_check_intr(sc);
538140923Sscottl	mtx_unlock(&sc->queue_mtx);
539114902Sscottl}
540114902Sscottl
541141062Sscottlvoid ips_morpheus_poll(ips_command_t *command)
542141062Sscottl{
543141062Sscottl	uint32_t ts;
544141062Sscottl
545145545Sscottl	/*
546145545Sscottl	 * Locks are not used here because this is only called during
547145545Sscottl	 * crashdumps.
548145545Sscottl	 */
549141062Sscottl	ts = time_second + command->timeout;
550141062Sscottl	while ((command->timeout != 0)
551141062Sscottl	 && (ips_morpheus_check_intr(command->sc) == 0)
552141062Sscottl	 && (ts > time_second))
553141062Sscottl		DELAY(1000);
554141062Sscottl}
555141062Sscottl
556114902Sscottlvoid ips_issue_morpheus_cmd(ips_command_t *command)
557114902Sscottl{
558114902Sscottl	/* hmmm, is there a cleaner way to do this? */
559114902Sscottl	if(command->sc->state & IPS_OFFLINE){
560150535Sscottl		ips_set_error(command, EINVAL);
561114902Sscottl		command->callback(command);
562114902Sscottl		return;
563114902Sscottl	}
564114902Sscottl	command->timeout = 10;
565114902Sscottl	ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr);
566114902Sscottl}
567114902Sscottl
568114902Sscottlstatic void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error)
569114902Sscottl{
570114902Sscottl	ips_copper_queue_t *queue = queueptr;
571114902Sscottl	if(error){
572114902Sscottl		return;
573114902Sscottl	}
574114902Sscottl	queue->base_phys_addr = segments[0].ds_addr;
575114902Sscottl}
576114902Sscottl
577114902Sscottlstatic int ips_copperhead_queue_init(ips_softc_t *sc)
578114902Sscottl{
579114902Sscottl	int error;
580114902Sscottl	bus_dma_tag_t dmatag;
581114902Sscottl	bus_dmamap_t dmamap;
582114902Sscottl       	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
583114902Sscottl				/* alignemnt */	1,
584114902Sscottl				/* boundary  */	0,
585114902Sscottl				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
586114902Sscottl				/* highaddr  */	BUS_SPACE_MAXADDR,
587114902Sscottl				/* filter    */	NULL,
588114902Sscottl				/* filterarg */	NULL,
589114902Sscottl				/* maxsize   */	sizeof(ips_copper_queue_t),
590114902Sscottl				/* numsegs   */	1,
591114902Sscottl				/* maxsegsize*/	sizeof(ips_copper_queue_t),
592114902Sscottl				/* flags     */	0,
593140923Sscottl				/* lockfunc  */ NULL,
594140923Sscottl				/* lockarg   */ NULL,
595114902Sscottl				&dmatag) != 0) {
596114902Sscottl                device_printf(sc->dev, "can't alloc dma tag for statue queue\n");
597114902Sscottl		error = ENOMEM;
598114902Sscottl		goto exit;
599114902Sscottl        }
600114902Sscottl	if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue),
601114902Sscottl	   		    BUS_DMA_NOWAIT, &dmamap)){
602114902Sscottl		error = ENOMEM;
603114902Sscottl		goto exit;
604114902Sscottl	}
605114902Sscottl	bzero(sc->copper_queue, sizeof(ips_copper_queue_t));
606114902Sscottl	sc->copper_queue->dmatag = dmatag;
607114902Sscottl	sc->copper_queue->dmamap = dmamap;
608114902Sscottl	sc->copper_queue->nextstatus = 1;
609114902Sscottl	bus_dmamap_load(dmatag, dmamap,
610114902Sscottl			&(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4,
611114902Sscottl			ips_copperhead_queue_callback, sc->copper_queue,
612114902Sscottl			BUS_DMA_NOWAIT);
613114902Sscottl	if(sc->copper_queue->base_phys_addr == 0){
614114902Sscottl		error = ENOMEM;
615114902Sscottl		goto exit;
616114902Sscottl	}
617114902Sscottl	ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr);
618114902Sscottl	ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr +
619114902Sscottl		    IPS_MAX_CMD_NUM * 4);
620114902Sscottl	ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4);
621114902Sscottl	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr);
622114902Sscottl
623114902Sscottl
624114902Sscottl	return 0;
625114902Sscottlexit:
626114902Sscottl	bus_dmamem_free(dmatag, sc->copper_queue, dmamap);
627114902Sscottl	bus_dma_tag_destroy(dmatag);
628114902Sscottl	return error;
629114902Sscottl}
630114902Sscottl
631114902Sscottl/* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */
632114902Sscottlint ips_copperhead_reinit(ips_softc_t *sc, int force)
633114902Sscottl{
634114902Sscottl	int i, j;
635114902Sscottl	u_int32_t postcode = 0, configstatus = 0;
636114902Sscottl	ips_write_1(sc, COPPER_REG_SCPR, 0x80);
637114902Sscottl	ips_write_1(sc, COPPER_REG_SCPR, 0);
638114902Sscottl	device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n");
639114902Sscottl	for(j = 0; j < 2; j++){
640114902Sscottl		postcode <<= 8;
641114902Sscottl		for(i = 0; i < 45; i++){
642114902Sscottl			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
643114902Sscottl				postcode |= ips_read_1(sc, COPPER_REG_ISPR);
644114902Sscottl				ips_write_1(sc, COPPER_REG_HISR,
645114902Sscottl					    COPPER_GHI_BIT);
646114902Sscottl				break;
647114902Sscottl			} else
648114902Sscottl				DELAY(1000000);
649114902Sscottl		}
650114902Sscottl		if(i == 45)
651114902Sscottl			return 1;
652114902Sscottl	}
653114902Sscottl	for(j = 0; j < 2; j++){
654114902Sscottl		configstatus <<= 8;
655114902Sscottl		for(i = 0; i < 240; i++){
656114902Sscottl			if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){
657114902Sscottl				configstatus |= ips_read_1(sc, COPPER_REG_ISPR);
658114902Sscottl				ips_write_1(sc, COPPER_REG_HISR,
659114902Sscottl					    COPPER_GHI_BIT);
660114902Sscottl				break;
661114902Sscottl			} else
662114902Sscottl				DELAY(1000000);
663114902Sscottl		}
664114902Sscottl		if(i == 240)
665114902Sscottl			return 1;
666114902Sscottl	}
667114902Sscottl	for(i = 0; i < 240; i++){
668114902Sscottl		if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){
669114902Sscottl			break;
670114902Sscottl		} else
671114902Sscottl			DELAY(1000000);
672114902Sscottl	}
673114902Sscottl	if(i == 240)
674114902Sscottl		return 1;
675114902Sscottl	ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT);
676114902Sscottl	ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT);
677114902Sscottl	ips_copperhead_queue_init(sc);
678114902Sscottl	ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT);
679114902Sscottl	i = ips_read_1(sc, COPPER_REG_SCPR);
680114902Sscottl	ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT);
681114902Sscottl	if(!configstatus){
682114902Sscottl		device_printf(sc->dev, "adapter initialization failed\n");
683114902Sscottl		return 1;
684114902Sscottl	}
685114902Sscottl	if(force && ips_clear_adapter(sc)){
686114902Sscottl		device_printf(sc->dev, "adapter clear failed\n");
687114902Sscottl		return 1;
688114902Sscottl	}
689114902Sscottl	return 0;
690114902Sscottl}
691114902Sscottlstatic u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc)
692114902Sscottl{
693114902Sscottl	u_int32_t value;
694114902Sscottl	int statnum = sc->copper_queue->nextstatus++;
695114902Sscottl	if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM)
696114902Sscottl		sc->copper_queue->nextstatus = 0;
697114902Sscottl	value = sc->copper_queue->status[statnum];
698114902Sscottl	ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr +
699114902Sscottl		    4 * statnum);
700114902Sscottl	return value;
701114902Sscottl}
702114902Sscottl
703114902Sscottl
704114902Sscottlvoid ips_copperhead_intr(void *void_sc)
705114902Sscottl{
706114902Sscottl        ips_softc_t *sc = (ips_softc_t *)void_sc;
707114902Sscottl	int cmdnumber;
708114902Sscottl	ips_cmd_status_t status;
709114902Sscottl
710140923Sscottl	mtx_lock(&sc->queue_mtx);
711114902Sscottl	while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){
712114902Sscottl		status.value = ips_copperhead_cmd_status(sc);
713114902Sscottl		cmdnumber = status.fields.command_id;
714114902Sscottl		sc->commandarray[cmdnumber].status.value = status.value;
715114902Sscottl		sc->commandarray[cmdnumber].timeout = 0;
716114902Sscottl		sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
717114902Sscottl		PRINTF(9, "ips: got command %d\n", cmdnumber);
718114902Sscottl	}
719140923Sscottl	mtx_unlock(&sc->queue_mtx);
720114902Sscottl        return;
721114902Sscottl}
722114902Sscottl
723114902Sscottlvoid ips_issue_copperhead_cmd(ips_command_t *command)
724114902Sscottl{
725114902Sscottl	int i;
726114902Sscottl	/* hmmm, is there a cleaner way to do this? */
727114902Sscottl	if(command->sc->state & IPS_OFFLINE){
728150535Sscottl		ips_set_error(command, EINVAL);
729114902Sscottl		command->callback(command);
730114902Sscottl		return;
731114902Sscottl	}
732114902Sscottl	command->timeout = 10;
733114902Sscottl	for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT;
734114902Sscottl	    i++ ){
735114902Sscottl		if( i == 20){
736114902Sscottlprintf("sem bit still set, can't send a command\n");
737114902Sscottl			return;
738114902Sscottl		}
739114902Sscottl		DELAY(500);/* need to do a delay here */
740114902Sscottl	}
741114902Sscottl	ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr);
742114902Sscottl	ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START);
743114902Sscottl}
744114902Sscottl
745141062Sscottlvoid ips_copperhead_poll(ips_command_t *command)
746141062Sscottl{
747141062Sscottl
748141062Sscottl	printf("ips: cmd polling not implemented for copperhead devices\n");
749141062Sscottl}
750