xref: /openbsd-src/sys/dev/ic/qla.c (revision a454aff3d36bc4380eb6cbe6ad399a96a9b74889)
1*a454aff3Snaddy /*	$OpenBSD: qla.c,v 1.69 2022/04/16 19:19:59 naddy Exp $ */
24cd89cf2Sjmatthew 
34cd89cf2Sjmatthew /*
44cd89cf2Sjmatthew  * Copyright (c) 2011 David Gwynne <dlg@openbsd.org>
54cd89cf2Sjmatthew  * Copyright (c) 2013, 2014 Jonathan Matthew <jmatthew@openbsd.org>
64cd89cf2Sjmatthew  *
74cd89cf2Sjmatthew  * Permission to use, copy, modify, and distribute this software for any
84cd89cf2Sjmatthew  * purpose with or without fee is hereby granted, provided that the above
94cd89cf2Sjmatthew  * copyright notice and this permission notice appear in all copies.
104cd89cf2Sjmatthew  *
114cd89cf2Sjmatthew  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
124cd89cf2Sjmatthew  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
134cd89cf2Sjmatthew  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
144cd89cf2Sjmatthew  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
154cd89cf2Sjmatthew  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
164cd89cf2Sjmatthew  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
174cd89cf2Sjmatthew  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
184cd89cf2Sjmatthew  */
194cd89cf2Sjmatthew 
204cd89cf2Sjmatthew #include <sys/param.h>
214cd89cf2Sjmatthew #include <sys/systm.h>
229a1bdb58Svisa #include <sys/atomic.h>
234cd89cf2Sjmatthew #include <sys/device.h>
244cd89cf2Sjmatthew #include <sys/ioctl.h>
254cd89cf2Sjmatthew #include <sys/malloc.h>
264cd89cf2Sjmatthew #include <sys/kernel.h>
274cd89cf2Sjmatthew #include <sys/mutex.h>
284cd89cf2Sjmatthew #include <sys/rwlock.h>
294cd89cf2Sjmatthew #include <sys/sensors.h>
304cd89cf2Sjmatthew #include <sys/queue.h>
314cd89cf2Sjmatthew 
324cd89cf2Sjmatthew #include <machine/bus.h>
334cd89cf2Sjmatthew 
344cd89cf2Sjmatthew #include <scsi/scsi_all.h>
354cd89cf2Sjmatthew #include <scsi/scsiconf.h>
364cd89cf2Sjmatthew 
374cd89cf2Sjmatthew #include <dev/ic/qlareg.h>
384cd89cf2Sjmatthew #include <dev/ic/qlavar.h>
394cd89cf2Sjmatthew 
40f901d880Sjmatthew #ifdef QLA_DEBUG
41f901d880Sjmatthew #define DPRINTF(m, f...) do { if ((qladebug & (m)) == (m)) printf(f); } \
42f901d880Sjmatthew     while (0)
43f901d880Sjmatthew #define QLA_D_MBOX		0x01
44f901d880Sjmatthew #define QLA_D_INTR		0x02
45f901d880Sjmatthew #define QLA_D_PORT		0x04
46f901d880Sjmatthew #define QLA_D_IO		0x08
47f901d880Sjmatthew #define QLA_D_IOCB		0x10
48f901d880Sjmatthew int qladebug = QLA_D_PORT;
49f901d880Sjmatthew #else
50f901d880Sjmatthew #define DPRINTF(m, f...)
51f901d880Sjmatthew #endif
52f901d880Sjmatthew 
53f901d880Sjmatthew 
54fa72eff0Sjmatthew #ifndef ISP_NOFIRMWARE
55a21e6caaSjmatthew #include <dev/microcode/isp/asm_2100.h>
56a21e6caaSjmatthew #include <dev/microcode/isp/asm_2200.h>
57a21e6caaSjmatthew #include <dev/microcode/isp/asm_2300.h>
58fa72eff0Sjmatthew #endif
594cd89cf2Sjmatthew 
604cd89cf2Sjmatthew struct cfdriver qla_cd = {
614cd89cf2Sjmatthew 	NULL,
624cd89cf2Sjmatthew 	"qla",
634cd89cf2Sjmatthew 	DV_DULL
644cd89cf2Sjmatthew };
654cd89cf2Sjmatthew 
664cd89cf2Sjmatthew void		qla_scsi_cmd(struct scsi_xfer *);
674cd89cf2Sjmatthew int		qla_scsi_probe(struct scsi_link *);
684cd89cf2Sjmatthew 
69b6400b02Sdlg u_int16_t	qla_read(struct qla_softc *, bus_size_t);
70b6400b02Sdlg void		qla_write(struct qla_softc *, bus_size_t, u_int16_t);
714cd89cf2Sjmatthew void		qla_host_cmd(struct qla_softc *sc, u_int16_t);
724cd89cf2Sjmatthew 
73b6400b02Sdlg u_int16_t	qla_read_queue_2100(struct qla_softc *, bus_size_t);
74b6400b02Sdlg 
7586376500Sjmatthew int		qla_mbox(struct qla_softc *, int);
764cd89cf2Sjmatthew int		qla_sns_req(struct qla_softc *, struct qla_dmamem *, int);
774cd89cf2Sjmatthew void		qla_mbox_putaddr(u_int16_t *, struct qla_dmamem *);
784cd89cf2Sjmatthew u_int16_t	qla_read_mbox(struct qla_softc *, int);
794cd89cf2Sjmatthew void		qla_write_mbox(struct qla_softc *, int, u_int16_t);
80aa14606aSjmatthew 
81aa14606aSjmatthew void		qla_handle_intr(struct qla_softc *, u_int16_t, u_int16_t);
824cd89cf2Sjmatthew void		qla_set_ints(struct qla_softc *, int);
8368858f63Sdlg int		qla_read_isr_1G(struct qla_softc *, u_int16_t *, u_int16_t *);
8468858f63Sdlg int		qla_read_isr_2G(struct qla_softc *, u_int16_t *, u_int16_t *);
85af65b7a1Sjmatthew void		qla_clear_isr(struct qla_softc *, u_int16_t);
86aa14606aSjmatthew 
87944e332aSjmatthew void		qla_update_start(struct qla_softc *, int);
88944e332aSjmatthew void		qla_update_done(struct qla_softc *, int);
89e4195480Sdlg void		qla_do_update(void *);
90944e332aSjmatthew 
914cd89cf2Sjmatthew void		qla_put_marker(struct qla_softc *, void *);
924cd89cf2Sjmatthew void		qla_put_cmd(struct qla_softc *, void *, struct scsi_xfer *,
934cd89cf2Sjmatthew 		    struct qla_ccb *);
944cd89cf2Sjmatthew struct qla_ccb *qla_handle_resp(struct qla_softc *, u_int16_t);
954cd89cf2Sjmatthew 
96d10165e5Sjmatthew int		qla_get_port_name_list(struct qla_softc *, u_int32_t);
97aa14606aSjmatthew struct qla_fc_port *qla_next_fabric_port(struct qla_softc *, u_int32_t *,
98aa14606aSjmatthew 		    u_int32_t *);
99944e332aSjmatthew int		qla_get_port_db(struct qla_softc *c, u_int16_t,
100944e332aSjmatthew 		    struct qla_dmamem *);
101944e332aSjmatthew int		qla_add_loop_port(struct qla_softc *, struct qla_fc_port *);
102944e332aSjmatthew int		qla_add_fabric_port(struct qla_softc *, struct qla_fc_port *);
10374521a28Sjmatthew int		qla_add_logged_in_port(struct qla_softc *, int, u_int32_t);
104aa14606aSjmatthew int		qla_classify_port(struct qla_softc *, u_int32_t, u_int64_t,
105944e332aSjmatthew 		    u_int64_t, struct qla_fc_port **);
10674521a28Sjmatthew int		qla_get_loop_id(struct qla_softc *sc, int);
107aa14606aSjmatthew void		qla_clear_port_lists(struct qla_softc *);
108aa14606aSjmatthew int		qla_softreset(struct qla_softc *);
109aa14606aSjmatthew void		qla_update_topology(struct qla_softc *);
110aa14606aSjmatthew int		qla_update_fabric(struct qla_softc *);
111aa14606aSjmatthew int		qla_fabric_plogi(struct qla_softc *, struct qla_fc_port *);
112aa14606aSjmatthew void		qla_fabric_plogo(struct qla_softc *, struct qla_fc_port *);
113aa14606aSjmatthew 
114aa14606aSjmatthew void		qla_update_start(struct qla_softc *, int);
115aa14606aSjmatthew int		qla_async(struct qla_softc *, u_int16_t);
116aa14606aSjmatthew 
1170732819bSjmatthew int		qla_verify_firmware(struct qla_softc *sc, u_int16_t);
1189eb00056Sjmatthew int		qla_load_firmware_words(struct qla_softc *, const u_int16_t *,
1199eb00056Sjmatthew 		    u_int16_t);
1209eb00056Sjmatthew int		qla_load_firmware_2100(struct qla_softc *);
121db1d60ccSjmatthew int		qla_load_firmware_2200(struct qla_softc *);
1224cd89cf2Sjmatthew int		qla_load_fwchunk_2300(struct qla_softc *,
1234cd89cf2Sjmatthew 		    struct qla_dmamem *, const u_int16_t *, u_int32_t);
1244cd89cf2Sjmatthew int		qla_load_firmware_2300(struct qla_softc *);
12533f4f584Sjmatthew int		qla_load_firmware_2322(struct qla_softc *);
1264cd89cf2Sjmatthew int		qla_read_nvram(struct qla_softc *);
1274cd89cf2Sjmatthew 
1284cd89cf2Sjmatthew struct qla_dmamem *qla_dmamem_alloc(struct qla_softc *, size_t);
1294cd89cf2Sjmatthew void		qla_dmamem_free(struct qla_softc *, struct qla_dmamem *);
1304cd89cf2Sjmatthew 
1314cd89cf2Sjmatthew int		qla_alloc_ccbs(struct qla_softc *);
1324cd89cf2Sjmatthew void		qla_free_ccbs(struct qla_softc *);
1334cd89cf2Sjmatthew void		*qla_get_ccb(void *);
1344cd89cf2Sjmatthew void		qla_put_ccb(void *, void *);
1354cd89cf2Sjmatthew 
1364cd89cf2Sjmatthew void		qla_dump_iocb(struct qla_softc *, void *);
1374cd89cf2Sjmatthew void		qla_dump_iocb_segs(struct qla_softc *, void *, int);
1384cd89cf2Sjmatthew 
13968858f63Sdlg static const struct qla_regs qla_regs_2100 = {
140b6400b02Sdlg 	qla_read_queue_2100,
14168858f63Sdlg 	qla_read_isr_1G,
142b6400b02Sdlg 	QLA_MBOX_BASE_2100 + 0x8,
143b6400b02Sdlg 	QLA_MBOX_BASE_2100 + 0x8,
144b6400b02Sdlg 	QLA_MBOX_BASE_2100 + 0xa,
145b6400b02Sdlg 	QLA_MBOX_BASE_2100 + 0xa
146b6400b02Sdlg };
147b6400b02Sdlg 
14868858f63Sdlg static const struct qla_regs qla_regs_2200 = {
149b6400b02Sdlg 	qla_read,
15068858f63Sdlg 	qla_read_isr_1G,
151b6400b02Sdlg 	QLA_MBOX_BASE_2200 + 0x8,
152b6400b02Sdlg 	QLA_MBOX_BASE_2200 + 0x8,
153b6400b02Sdlg 	QLA_MBOX_BASE_2200 + 0xa,
154b6400b02Sdlg 	QLA_MBOX_BASE_2200 + 0xa
155b6400b02Sdlg };
156b6400b02Sdlg 
15768858f63Sdlg static const struct qla_regs qla_regs_23XX = {
158b6400b02Sdlg 	qla_read,
15968858f63Sdlg 	qla_read_isr_2G,
160b6400b02Sdlg 	QLA_REQ_IN,
161b6400b02Sdlg 	QLA_REQ_OUT,
162b6400b02Sdlg 	QLA_RESP_IN,
163b6400b02Sdlg 	QLA_RESP_OUT
164b6400b02Sdlg };
165b6400b02Sdlg 
16668858f63Sdlg #define qla_queue_read(_sc, _r) ((*(_sc)->sc_regs->read)((_sc), (_r)))
167b6400b02Sdlg #define qla_queue_write(_sc, _r, _v) qla_write((_sc), (_r), (_v))
168b6400b02Sdlg 
16968858f63Sdlg #define qla_read_isr(_sc, _isr, _info) \
17068858f63Sdlg     ((*(_sc)->sc_regs->read_isr)((_sc), (_isr), (_info)))
17168858f63Sdlg 
172*a454aff3Snaddy const struct scsi_adapter qla_switch = {
17321ceeee0Skrw 	qla_scsi_cmd, NULL, qla_scsi_probe, NULL, NULL
1744cd89cf2Sjmatthew };
1754cd89cf2Sjmatthew 
1764cd89cf2Sjmatthew int
qla_classify_port(struct qla_softc * sc,u_int32_t location,u_int64_t port_name,u_int64_t node_name,struct qla_fc_port ** prev)1774cd89cf2Sjmatthew qla_classify_port(struct qla_softc *sc, u_int32_t location,
178944e332aSjmatthew     u_int64_t port_name, u_int64_t node_name, struct qla_fc_port **prev)
1794cd89cf2Sjmatthew {
1804cd89cf2Sjmatthew 	struct qla_fc_port *port, *locmatch, *wwnmatch;
1814cd89cf2Sjmatthew 	locmatch = NULL;
1824cd89cf2Sjmatthew 	wwnmatch = NULL;
1834cd89cf2Sjmatthew 
1844cd89cf2Sjmatthew 	/* make sure we don't try to add a port or location twice */
1854cd89cf2Sjmatthew 	TAILQ_FOREACH(port, &sc->sc_ports_new, update) {
1864cd89cf2Sjmatthew 		if ((port->port_name == port_name &&
1874cd89cf2Sjmatthew 		    port->node_name == node_name) ||
188944e332aSjmatthew 		    port->location == location) {
189944e332aSjmatthew 			*prev = port;
1904cd89cf2Sjmatthew 			return (QLA_PORT_DISP_DUP);
1914cd89cf2Sjmatthew 		}
192944e332aSjmatthew 	}
1934cd89cf2Sjmatthew 
1944cd89cf2Sjmatthew 	/* if we're attaching, everything is new */
195944e332aSjmatthew 	if (sc->sc_scsibus == NULL) {
196944e332aSjmatthew 		*prev = NULL;
1974cd89cf2Sjmatthew 		return (QLA_PORT_DISP_NEW);
198944e332aSjmatthew 	}
1994cd89cf2Sjmatthew 
200944e332aSjmatthew 	TAILQ_FOREACH(port, &sc->sc_ports, ports) {
2014cd89cf2Sjmatthew 		if (port->location == location)
2024cd89cf2Sjmatthew 			locmatch = port;
2034cd89cf2Sjmatthew 
2044cd89cf2Sjmatthew 		if (port->port_name == port_name &&
2054cd89cf2Sjmatthew 		    port->node_name == node_name)
2064cd89cf2Sjmatthew 			wwnmatch = port;
2074cd89cf2Sjmatthew 	}
2084cd89cf2Sjmatthew 
2094cd89cf2Sjmatthew 	if (locmatch == NULL && wwnmatch == NULL) {
210944e332aSjmatthew 		*prev = NULL;
2114cd89cf2Sjmatthew 		return (QLA_PORT_DISP_NEW);
2124cd89cf2Sjmatthew 	} else if (locmatch == wwnmatch) {
213944e332aSjmatthew 		*prev = locmatch;
2144cd89cf2Sjmatthew 		return (QLA_PORT_DISP_SAME);
2154cd89cf2Sjmatthew 	} else if (wwnmatch != NULL) {
216944e332aSjmatthew 		*prev = wwnmatch;
2174cd89cf2Sjmatthew 		return (QLA_PORT_DISP_MOVED);
2184cd89cf2Sjmatthew 	} else {
219944e332aSjmatthew 		*prev = locmatch;
2204cd89cf2Sjmatthew 		return (QLA_PORT_DISP_CHANGED);
2214cd89cf2Sjmatthew 	}
2224cd89cf2Sjmatthew }
2234cd89cf2Sjmatthew 
2244cd89cf2Sjmatthew int
qla_get_loop_id(struct qla_softc * sc,int start)22574521a28Sjmatthew qla_get_loop_id(struct qla_softc *sc, int start)
2264cd89cf2Sjmatthew {
2274cd89cf2Sjmatthew 	int i, last;
2284cd89cf2Sjmatthew 
2294cd89cf2Sjmatthew 	if (sc->sc_2k_logins) {
2304cd89cf2Sjmatthew 		i = QLA_2KL_MIN_HANDLE;
2314cd89cf2Sjmatthew 		last = QLA_2KL_MAX_HANDLE;
2324cd89cf2Sjmatthew 	} else {
2334cd89cf2Sjmatthew 		/* if we're an F port, we can have two ranges, but meh */
2344cd89cf2Sjmatthew 		i = QLA_MIN_HANDLE;
2354cd89cf2Sjmatthew 		last = QLA_MAX_HANDLE;
2364cd89cf2Sjmatthew 	}
23774521a28Sjmatthew 	if (i < start)
23874521a28Sjmatthew 		i = start;
23974521a28Sjmatthew 
2404cd89cf2Sjmatthew 	for (; i <= last; i++) {
2414cd89cf2Sjmatthew 		if (sc->sc_targets[i] == NULL)
2424cd89cf2Sjmatthew 			return (i);
2434cd89cf2Sjmatthew 	}
2444cd89cf2Sjmatthew 
2454cd89cf2Sjmatthew 	return (-1);
2464cd89cf2Sjmatthew }
2474cd89cf2Sjmatthew 
2484cd89cf2Sjmatthew int
qla_get_port_db(struct qla_softc * sc,u_int16_t loopid,struct qla_dmamem * mem)249944e332aSjmatthew qla_get_port_db(struct qla_softc *sc, u_int16_t loopid, struct qla_dmamem *mem)
2504cd89cf2Sjmatthew {
2514cd89cf2Sjmatthew 	sc->sc_mbox[0] = QLA_MBOX_GET_PORT_DB;
2524cd89cf2Sjmatthew 	if (sc->sc_2k_logins) {
2534cd89cf2Sjmatthew 		sc->sc_mbox[1] = loopid;
2544cd89cf2Sjmatthew 	} else {
2554cd89cf2Sjmatthew 		sc->sc_mbox[1] = loopid << 8;
2564cd89cf2Sjmatthew 	}
257aa14606aSjmatthew 
258944e332aSjmatthew 	memset(QLA_DMA_KVA(mem), 0, sizeof(struct qla_get_port_db));
259944e332aSjmatthew 	qla_mbox_putaddr(sc->sc_mbox, mem);
260944e332aSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0,
2614cd89cf2Sjmatthew 	    sizeof(struct qla_get_port_db), BUS_DMASYNC_PREREAD);
26286376500Sjmatthew 	if (qla_mbox(sc, 0x00cf)) {
263d0f795e9Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: get port db %d failed: %x\n",
2644cd89cf2Sjmatthew 		    DEVNAME(sc), loopid, sc->sc_mbox[0]);
2654cd89cf2Sjmatthew 		return (1);
2664cd89cf2Sjmatthew 	}
2674cd89cf2Sjmatthew 
268944e332aSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0,
269944e332aSjmatthew 	    sizeof(struct qla_get_port_db), BUS_DMASYNC_POSTREAD);
270944e332aSjmatthew 	return (0);
271944e332aSjmatthew }
2724cd89cf2Sjmatthew 
273944e332aSjmatthew int
qla_add_loop_port(struct qla_softc * sc,struct qla_fc_port * port)274944e332aSjmatthew qla_add_loop_port(struct qla_softc *sc, struct qla_fc_port *port)
275944e332aSjmatthew {
276944e332aSjmatthew 	struct qla_get_port_db *pdb;
277944e332aSjmatthew 	struct qla_fc_port *pport = NULL;
278944e332aSjmatthew 	int disp;
279944e332aSjmatthew 
280944e332aSjmatthew 	if (qla_get_port_db(sc, port->loopid, sc->sc_scratch)) {
281944e332aSjmatthew 		return (1);
282944e332aSjmatthew 	}
283944e332aSjmatthew 	pdb = QLA_DMA_KVA(sc->sc_scratch);
284944e332aSjmatthew 
285944e332aSjmatthew 	if (letoh16(pdb->prli_svc_word3) & QLA_SVC3_TARGET_ROLE)
286944e332aSjmatthew 		port->flags |= QLA_PORT_FLAG_IS_TARGET;
287944e332aSjmatthew 
288944e332aSjmatthew 	port->port_name = betoh64(pdb->port_name);
289944e332aSjmatthew 	port->node_name = betoh64(pdb->node_name);
290944e332aSjmatthew 	port->portid = (letoh16(pdb->port_id[0]) << 16) |
291944e332aSjmatthew 	    letoh16(pdb->port_id[1]);
292944e332aSjmatthew 
293944e332aSjmatthew 	mtx_enter(&sc->sc_port_mtx);
294944e332aSjmatthew 	disp = qla_classify_port(sc, port->location, port->port_name,
295944e332aSjmatthew 	    port->node_name, &pport);
296944e332aSjmatthew 	switch (disp) {
297944e332aSjmatthew 	case QLA_PORT_DISP_CHANGED:
298944e332aSjmatthew 	case QLA_PORT_DISP_MOVED:
299944e332aSjmatthew 	case QLA_PORT_DISP_NEW:
300944e332aSjmatthew 		TAILQ_INSERT_TAIL(&sc->sc_ports_new, port, update);
301944e332aSjmatthew 		sc->sc_targets[port->loopid] = port;
302944e332aSjmatthew 		break;
303944e332aSjmatthew 	case QLA_PORT_DISP_DUP:
304289dc1f7Sderaadt 		free(port, M_DEVBUF, sizeof *port);
305944e332aSjmatthew 		break;
306944e332aSjmatthew 	case QLA_PORT_DISP_SAME:
307944e332aSjmatthew 		TAILQ_REMOVE(&sc->sc_ports_gone, pport, update);
308289dc1f7Sderaadt 		free(port, M_DEVBUF, sizeof *port);
309944e332aSjmatthew 		break;
310944e332aSjmatthew 	}
311944e332aSjmatthew 	mtx_leave(&sc->sc_port_mtx);
312944e332aSjmatthew 
313944e332aSjmatthew 	switch (disp) {
314944e332aSjmatthew 	case QLA_PORT_DISP_CHANGED:
315944e332aSjmatthew 	case QLA_PORT_DISP_MOVED:
316944e332aSjmatthew 	case QLA_PORT_DISP_NEW:
317944e332aSjmatthew 		DPRINTF(QLA_D_PORT, "%s: %s %d; name %llx, port %06x\n",
318944e332aSjmatthew 		    DEVNAME(sc), ISSET(port->flags, QLA_PORT_FLAG_IS_TARGET) ?
319944e332aSjmatthew 		    "target" : "non-target", port->loopid, port->port_name,
320944e332aSjmatthew 		    port->portid);
321944e332aSjmatthew 		break;
322944e332aSjmatthew 	}
323944e332aSjmatthew 	return (0);
324944e332aSjmatthew }
325944e332aSjmatthew 
326944e332aSjmatthew int
qla_add_fabric_port(struct qla_softc * sc,struct qla_fc_port * port)327944e332aSjmatthew qla_add_fabric_port(struct qla_softc *sc, struct qla_fc_port *port)
328944e332aSjmatthew {
329944e332aSjmatthew 	struct qla_get_port_db *pdb;
330944e332aSjmatthew 
331944e332aSjmatthew 	if (qla_get_port_db(sc, port->loopid, sc->sc_scratch)) {
332944e332aSjmatthew 		return (1);
333944e332aSjmatthew 	}
334944e332aSjmatthew 	pdb = QLA_DMA_KVA(sc->sc_scratch);
335944e332aSjmatthew 
336944e332aSjmatthew 	if (letoh16(pdb->prli_svc_word3) & QLA_SVC3_TARGET_ROLE)
337944e332aSjmatthew 		port->flags |= QLA_PORT_FLAG_IS_TARGET;
338944e332aSjmatthew 
339944e332aSjmatthew 	/*
340944e332aSjmatthew 	 * if we only know about this port because qla_get_port_name_list
341944e332aSjmatthew 	 * returned it, we don't have its port id or node name, so fill
342944e332aSjmatthew 	 * those in and update its location.
3434cd89cf2Sjmatthew 	 */
344944e332aSjmatthew 	if (port->location == QLA_LOCATION_FABRIC) {
345944e332aSjmatthew 		port->node_name = betoh64(pdb->node_name);
34674521a28Sjmatthew 		port->port_name = betoh64(pdb->port_name);
347944e332aSjmatthew 		port->portid = (letoh16(pdb->port_id[0]) << 16) |
348944e332aSjmatthew 		    letoh16(pdb->port_id[1]);
349944e332aSjmatthew 		port->location = QLA_LOCATION_PORT_ID(port->portid);
350944e332aSjmatthew 	}
351944e332aSjmatthew 
352944e332aSjmatthew 	mtx_enter(&sc->sc_port_mtx);
353944e332aSjmatthew 	TAILQ_INSERT_TAIL(&sc->sc_ports_new, port, update);
354944e332aSjmatthew 	sc->sc_targets[port->loopid] = port;
355944e332aSjmatthew 	mtx_leave(&sc->sc_port_mtx);
356944e332aSjmatthew 
357944e332aSjmatthew 	DPRINTF(QLA_D_PORT, "%s: %s %d; name %llx\n",
358944e332aSjmatthew 	    DEVNAME(sc), ISSET(port->flags, QLA_PORT_FLAG_IS_TARGET) ?
359944e332aSjmatthew 	    "target" : "non-target", port->loopid, port->port_name);
360944e332aSjmatthew 	return (0);
361944e332aSjmatthew }
362944e332aSjmatthew 
363944e332aSjmatthew int
qla_add_logged_in_port(struct qla_softc * sc,int loopid,u_int32_t portid)36474521a28Sjmatthew qla_add_logged_in_port(struct qla_softc *sc, int loopid, u_int32_t portid)
365944e332aSjmatthew {
366944e332aSjmatthew 	struct qla_fc_port *port;
36774521a28Sjmatthew 	struct qla_get_port_db *pdb;
36874521a28Sjmatthew 	u_int64_t node_name, port_name;
36974521a28Sjmatthew 	int flags, ret;
37074521a28Sjmatthew 
37174521a28Sjmatthew 	ret = qla_get_port_db(sc, loopid, sc->sc_scratch);
37274521a28Sjmatthew 	mtx_enter(&sc->sc_port_mtx);
37374521a28Sjmatthew 	if (ret != 0) {
37474521a28Sjmatthew 		/* put in a fake port to prevent use of this loop id */
37574521a28Sjmatthew 		printf("%s: loop id %d used, but can't see what's using it\n",
37674521a28Sjmatthew 		    DEVNAME(sc), loopid);
37774521a28Sjmatthew 		node_name = 0;
37874521a28Sjmatthew 		port_name = 0;
37974521a28Sjmatthew 		flags = 0;
38074521a28Sjmatthew 	} else {
38174521a28Sjmatthew 		pdb = QLA_DMA_KVA(sc->sc_scratch);
38274521a28Sjmatthew 		node_name = betoh64(pdb->node_name);
38374521a28Sjmatthew 		port_name = betoh64(pdb->port_name);
38474521a28Sjmatthew 		flags = 0;
38574521a28Sjmatthew 		if (letoh16(pdb->prli_svc_word3) & QLA_SVC3_TARGET_ROLE)
38674521a28Sjmatthew 			flags |= QLA_PORT_FLAG_IS_TARGET;
38774521a28Sjmatthew 
38874521a28Sjmatthew 		/* see if we've already found this port */
38974521a28Sjmatthew 		TAILQ_FOREACH(port, &sc->sc_ports_found, update) {
39074521a28Sjmatthew 			if ((port->node_name == node_name) &&
39174521a28Sjmatthew 			    (port->port_name == port_name) &&
39274521a28Sjmatthew 			    (port->portid == portid)) {
39374521a28Sjmatthew 				mtx_leave(&sc->sc_port_mtx);
39474521a28Sjmatthew 				DPRINTF(QLA_D_PORT, "%s: already found port "
39574521a28Sjmatthew 				    "%06x\n", DEVNAME(sc), portid);
39674521a28Sjmatthew 				return (0);
39774521a28Sjmatthew 			}
39874521a28Sjmatthew 		}
39974521a28Sjmatthew 	}
4004cd89cf2Sjmatthew 
4014cd89cf2Sjmatthew 	port = malloc(sizeof(*port), M_DEVBUF, M_ZERO | M_NOWAIT);
4024cd89cf2Sjmatthew 	if (port == NULL) {
40374521a28Sjmatthew 		mtx_leave(&sc->sc_port_mtx);
4044cd89cf2Sjmatthew 		printf("%s: failed to allocate a port structure\n",
4054cd89cf2Sjmatthew 		    DEVNAME(sc));
4064cd89cf2Sjmatthew 		return (1);
4074cd89cf2Sjmatthew 	}
408a8e6d273Sjmatthew 	port->location = QLA_LOCATION_PORT_ID(portid);
40974521a28Sjmatthew 	port->port_name = port_name;
41074521a28Sjmatthew 	port->node_name = node_name;
4114cd89cf2Sjmatthew 	port->loopid = loopid;
4124cd89cf2Sjmatthew 	port->portid = portid;
41374521a28Sjmatthew 	port->flags = flags;
4144cd89cf2Sjmatthew 
415944e332aSjmatthew 	TAILQ_INSERT_TAIL(&sc->sc_ports, port, ports);
416944e332aSjmatthew 	sc->sc_targets[port->loopid] = port;
4174cd89cf2Sjmatthew 	mtx_leave(&sc->sc_port_mtx);
4184cd89cf2Sjmatthew 
41974521a28Sjmatthew 	DPRINTF(QLA_D_PORT, "%s: added logged in port %06x at %d\n",
420944e332aSjmatthew 	    DEVNAME(sc), portid, loopid);
4214cd89cf2Sjmatthew 	return (0);
4224cd89cf2Sjmatthew }
4234cd89cf2Sjmatthew 
4244cd89cf2Sjmatthew int
qla_attach(struct qla_softc * sc)4254cd89cf2Sjmatthew qla_attach(struct qla_softc *sc)
4264cd89cf2Sjmatthew {
4274cd89cf2Sjmatthew 	struct scsibus_attach_args saa;
4284cd89cf2Sjmatthew 	struct qla_init_cb *icb;
42995622914Sderaadt #ifndef ISP_NOFIRMWARE
43095622914Sderaadt 	int (*loadfirmware)(struct qla_softc *) = NULL;
43195622914Sderaadt #endif
4320732819bSjmatthew 	u_int16_t firmware_addr = 0;
43391637d79Sdlg 	u_int64_t dva;
4344cd89cf2Sjmatthew 	int i, rv;
4354cd89cf2Sjmatthew 
4364cd89cf2Sjmatthew 	TAILQ_INIT(&sc->sc_ports);
4374cd89cf2Sjmatthew 	TAILQ_INIT(&sc->sc_ports_new);
4384cd89cf2Sjmatthew 	TAILQ_INIT(&sc->sc_ports_gone);
439d10165e5Sjmatthew 	TAILQ_INIT(&sc->sc_ports_found);
4404cd89cf2Sjmatthew 
4414cd89cf2Sjmatthew 	switch (sc->sc_isp_gen) {
4424cd89cf2Sjmatthew 	case QLA_GEN_ISP2100:
4439eb00056Sjmatthew 		sc->sc_mbox_base = QLA_MBOX_BASE_2100;
44468858f63Sdlg 		sc->sc_regs = &qla_regs_2100;
44595622914Sderaadt #ifndef ISP_NOFIRMWARE
44695622914Sderaadt 		loadfirmware = qla_load_firmware_2100;
44795622914Sderaadt #endif
4480732819bSjmatthew 		firmware_addr = QLA_2100_CODE_ORG;
4499eb00056Sjmatthew 		break;
4504cd89cf2Sjmatthew 
451db1d60ccSjmatthew 	case QLA_GEN_ISP2200:
452db1d60ccSjmatthew 		sc->sc_mbox_base = QLA_MBOX_BASE_2200;
45368858f63Sdlg 		sc->sc_regs = &qla_regs_2200;
45495622914Sderaadt #ifndef ISP_NOFIRMWARE
45595622914Sderaadt 		loadfirmware = qla_load_firmware_2200;
45695622914Sderaadt #endif
4570732819bSjmatthew 		firmware_addr = QLA_2200_CODE_ORG;
458db1d60ccSjmatthew 		break;
459db1d60ccSjmatthew 
4604cd89cf2Sjmatthew 	case QLA_GEN_ISP23XX:
4614cd89cf2Sjmatthew 		sc->sc_mbox_base = QLA_MBOX_BASE_23XX;
46268858f63Sdlg 		sc->sc_regs = &qla_regs_23XX;
46395622914Sderaadt #ifndef ISP_NOFIRMWARE
46433f4f584Sjmatthew 		if (sc->sc_isp_type != QLA_ISP2322)
46595622914Sderaadt 			loadfirmware = qla_load_firmware_2300;
46695622914Sderaadt #endif
4670732819bSjmatthew 		firmware_addr = QLA_2300_CODE_ORG;
4684cd89cf2Sjmatthew 		break;
4694cd89cf2Sjmatthew 
4704cd89cf2Sjmatthew 	default:
4714cd89cf2Sjmatthew 		printf("unknown isp type\n");
4724cd89cf2Sjmatthew 		return (ENXIO);
4734cd89cf2Sjmatthew 	}
4744cd89cf2Sjmatthew 
4754cd89cf2Sjmatthew 	/* after reset, mbox registers 1-3 should contain the string "ISP   " */
4764cd89cf2Sjmatthew 	if (qla_read_mbox(sc, 1) != 0x4953 ||
4774cd89cf2Sjmatthew 	    qla_read_mbox(sc, 2) != 0x5020 ||
4784cd89cf2Sjmatthew 	    qla_read_mbox(sc, 3) != 0x2020) {
4794cd89cf2Sjmatthew 		/* try releasing the risc processor */
4804cd89cf2Sjmatthew 		qla_host_cmd(sc, QLA_HOST_CMD_RELEASE);
4814cd89cf2Sjmatthew 	}
4824cd89cf2Sjmatthew 
4834cd89cf2Sjmatthew 	qla_host_cmd(sc, QLA_HOST_CMD_PAUSE);
484aa14606aSjmatthew 	if (qla_softreset(sc) != 0) {
485aa14606aSjmatthew 		printf("softreset failed\n");
4864cd89cf2Sjmatthew 		return (ENXIO);
4874cd89cf2Sjmatthew 	}
4884cd89cf2Sjmatthew 
48907286c37Skettenis 	if (qla_read_nvram(sc) == 0) {
4904cd89cf2Sjmatthew 		sc->sc_nvram_valid = 1;
49107286c37Skettenis 		if (sc->sc_port_name == 0)
49207286c37Skettenis 			sc->sc_port_name = betoh64(sc->sc_nvram.port_name);
49307286c37Skettenis 		if (sc->sc_node_name == 0)
49407286c37Skettenis 			sc->sc_node_name = betoh64(sc->sc_nvram.node_name);
49507286c37Skettenis 	}
49607286c37Skettenis 
49707286c37Skettenis 	if (sc->sc_port_name == 0)
49807286c37Skettenis 		sc->sc_port_name = QLA_DEFAULT_PORT_NAME;
499db1d60ccSjmatthew 
5000732819bSjmatthew #ifdef ISP_NOFIRMWARE
5010732819bSjmatthew 	if (qla_verify_firmware(sc, firmware_addr)) {
5020732819bSjmatthew 		printf("%s: no firmware loaded\n", DEVNAME(sc));
5030732819bSjmatthew 		return (ENXIO);
5040732819bSjmatthew 	}
5050732819bSjmatthew #else
50695622914Sderaadt 	if (loadfirmware && (loadfirmware)(sc)) {
5070732819bSjmatthew 		printf("%s: firmware load failed\n", DEVNAME(sc));
5089eb00056Sjmatthew 		return (ENXIO);
5099eb00056Sjmatthew 	}
51095622914Sderaadt #endif
5114cd89cf2Sjmatthew 
5124cd89cf2Sjmatthew 	/* execute firmware */
5134cd89cf2Sjmatthew 	sc->sc_mbox[0] = QLA_MBOX_EXEC_FIRMWARE;
5140732819bSjmatthew 	sc->sc_mbox[1] = firmware_addr;
51533f4f584Sjmatthew #ifdef ISP_NOFIRMWARE
51633f4f584Sjmatthew 	sc->sc_mbox[2] = 1;
51733f4f584Sjmatthew #else
51833f4f584Sjmatthew 	if (loadfirmware)
51933f4f584Sjmatthew 		sc->sc_mbox[2] = 0;
52033f4f584Sjmatthew 	else
52133f4f584Sjmatthew 		sc->sc_mbox[2] = 1;
52233f4f584Sjmatthew #endif
52386376500Sjmatthew 	if (qla_mbox(sc, 0x0007)) {
5244cd89cf2Sjmatthew 		printf("ISP couldn't exec firmware: %x\n", sc->sc_mbox[0]);
5254cd89cf2Sjmatthew 		return (ENXIO);
5264cd89cf2Sjmatthew 	}
5274cd89cf2Sjmatthew 
5284cd89cf2Sjmatthew 	delay(250000);		/* from isp(4) */
5294cd89cf2Sjmatthew 
5304cd89cf2Sjmatthew 	sc->sc_mbox[0] = QLA_MBOX_ABOUT_FIRMWARE;
53186376500Sjmatthew 	if (qla_mbox(sc, 0x0001)) {
5324cd89cf2Sjmatthew 		printf("ISP not talking after firmware exec: %x\n",
5334cd89cf2Sjmatthew 		    sc->sc_mbox[0]);
5344cd89cf2Sjmatthew 		return (ENXIO);
5354cd89cf2Sjmatthew 	}
5360fb098c3Skettenis 	printf("%s: firmware rev %d.%d.%d, attrs 0x%x\n", DEVNAME(sc),
5370fb098c3Skettenis 	    sc->sc_mbox[1], sc->sc_mbox[2], sc->sc_mbox[3], sc->sc_mbox[6]);
5384cd89cf2Sjmatthew 
5394cd89cf2Sjmatthew 	if (sc->sc_mbox[6] & QLA_FW_ATTR_EXPANDED_LUN)
5404cd89cf2Sjmatthew 		sc->sc_expanded_lun = 1;
5414cd89cf2Sjmatthew 	if (sc->sc_mbox[6] & QLA_FW_ATTR_FABRIC)
5424cd89cf2Sjmatthew 		sc->sc_fabric = 1;
5434cd89cf2Sjmatthew 	if (sc->sc_mbox[6] & QLA_FW_ATTR_2K_LOGINS)
5444cd89cf2Sjmatthew 		sc->sc_2k_logins = 1;
5454cd89cf2Sjmatthew 
5464cd89cf2Sjmatthew 	/* work out how many ccbs to allocate */
5474cd89cf2Sjmatthew 	sc->sc_mbox[0] = QLA_MBOX_GET_FIRMWARE_STATUS;
54886376500Sjmatthew 	if (qla_mbox(sc, 0x0001)) {
5494cd89cf2Sjmatthew 		printf("couldn't get firmware status: %x\n", sc->sc_mbox[0]);
5504cd89cf2Sjmatthew 		return (ENXIO);
5514cd89cf2Sjmatthew 	}
5524cd89cf2Sjmatthew 	sc->sc_maxcmds = sc->sc_mbox[2];
5534cd89cf2Sjmatthew 
5544cd89cf2Sjmatthew 	if (qla_alloc_ccbs(sc)) {
5554cd89cf2Sjmatthew 		/* error already printed */
5564cd89cf2Sjmatthew 		return (ENOMEM);
5574cd89cf2Sjmatthew 	}
5584cd89cf2Sjmatthew 	sc->sc_scratch = qla_dmamem_alloc(sc, QLA_SCRATCH_SIZE);
5594cd89cf2Sjmatthew 	if (sc->sc_scratch == NULL) {
5604cd89cf2Sjmatthew 		printf("%s: unable to allocate scratch\n", DEVNAME(sc));
5614cd89cf2Sjmatthew 		goto free_ccbs;
5624cd89cf2Sjmatthew 	}
5634cd89cf2Sjmatthew 
5644cd89cf2Sjmatthew 	/* build init buffer thing */
5654cd89cf2Sjmatthew 	icb = (struct qla_init_cb *)QLA_DMA_KVA(sc->sc_scratch);
5664cd89cf2Sjmatthew 	memset(icb, 0, sizeof(*icb));
5674cd89cf2Sjmatthew 	icb->icb_version = QLA_ICB_VERSION;
56807286c37Skettenis 	/* port and node names are big-endian in the icb */
56991637d79Sdlg 	htobem32(&icb->icb_portname_hi, sc->sc_port_name >> 32);
57091637d79Sdlg 	htobem32(&icb->icb_portname_lo, sc->sc_port_name);
57191637d79Sdlg 	htobem32(&icb->icb_nodename_hi, sc->sc_node_name >> 32);
57291637d79Sdlg 	htobem32(&icb->icb_nodename_lo, sc->sc_node_name);
5734cd89cf2Sjmatthew 	if (sc->sc_nvram_valid) {
5744cd89cf2Sjmatthew 		icb->icb_fw_options = sc->sc_nvram.fw_options;
5754cd89cf2Sjmatthew 		icb->icb_max_frame_len = sc->sc_nvram.frame_payload_size;
5764cd89cf2Sjmatthew 		icb->icb_max_alloc = sc->sc_nvram.max_iocb_allocation;
5774cd89cf2Sjmatthew 		icb->icb_exec_throttle = sc->sc_nvram.execution_throttle;
5784cd89cf2Sjmatthew 		icb->icb_retry_count = sc->sc_nvram.retry_count;
5794cd89cf2Sjmatthew 		icb->icb_retry_delay = sc->sc_nvram.retry_delay;
5804cd89cf2Sjmatthew 		icb->icb_hardaddr = sc->sc_nvram.hard_address;
5814cd89cf2Sjmatthew 		icb->icb_inquiry_data = sc->sc_nvram.inquiry_data;
5824cd89cf2Sjmatthew 		icb->icb_login_timeout = sc->sc_nvram.login_timeout;
5834cd89cf2Sjmatthew 		icb->icb_xfwoptions = sc->sc_nvram.add_fw_options;
5844cd89cf2Sjmatthew 		icb->icb_zfwoptions = sc->sc_nvram.special_options;
5854cd89cf2Sjmatthew 	} else {
5864cd89cf2Sjmatthew 		/* defaults copied from isp(4) */
5874cd89cf2Sjmatthew 		icb->icb_retry_count = 3;
5884cd89cf2Sjmatthew 		icb->icb_retry_delay = 5;
5894cd89cf2Sjmatthew 		icb->icb_exec_throttle = htole16(16);
5904cd89cf2Sjmatthew 		icb->icb_max_alloc = htole16(256);
5914cd89cf2Sjmatthew 		icb->icb_max_frame_len = htole16(1024);
5924cd89cf2Sjmatthew 		icb->icb_fw_options = htole16(QLA_ICB_FW_FAIRNESS |
5934cd89cf2Sjmatthew 		    QLA_ICB_FW_ENABLE_PDB_CHANGED | QLA_ICB_FW_HARD_ADDR |
5944cd89cf2Sjmatthew 		    QLA_ICB_FW_FULL_DUPLEX);
5954cd89cf2Sjmatthew 	}
5964cd89cf2Sjmatthew 	/* target mode stuff that we don't care about */
5974cd89cf2Sjmatthew 	icb->icb_lun_enables = 0;
5984cd89cf2Sjmatthew 	icb->icb_cmd_count = 0;
5994cd89cf2Sjmatthew 	icb->icb_notify_count = 0;
6004cd89cf2Sjmatthew 	icb->icb_lun_timeout = 0;
6014cd89cf2Sjmatthew 
6024cd89cf2Sjmatthew 	/* "zero interrupt operation" */
6034cd89cf2Sjmatthew 	icb->icb_int_delaytimer = 0;
6044cd89cf2Sjmatthew 
6054cd89cf2Sjmatthew 	icb->icb_req_out = 0;
6064cd89cf2Sjmatthew 	icb->icb_resp_in = 0;
60791637d79Sdlg 	htolem16(&icb->icb_req_queue_len, sc->sc_maxcmds);
60891637d79Sdlg 	htolem16(&icb->icb_resp_queue_len, sc->sc_maxcmds);
60991637d79Sdlg 	dva = QLA_DMA_DVA(sc->sc_requests);
61091637d79Sdlg 	htolem32(&icb->icb_req_queue_addr_lo, dva);
61191637d79Sdlg 	htolem32(&icb->icb_req_queue_addr_hi, dva >> 32);
61291637d79Sdlg 	dva = QLA_DMA_DVA(sc->sc_responses);
61391637d79Sdlg 	htolem32(&icb->icb_resp_queue_addr_lo, dva);
61491637d79Sdlg 	htolem32(&icb->icb_resp_queue_addr_hi, dva >> 32);
6154cd89cf2Sjmatthew 
6164cd89cf2Sjmatthew 	/* adjust firmware options a bit */
6174cd89cf2Sjmatthew 	icb->icb_fw_options |= htole16(QLA_ICB_FW_EXTENDED_INIT_CB);
61800ef04b4Sjmatthew 	icb->icb_fw_options &= ~htole16(QLA_ICB_FW_FAST_POST);
6194cd89cf2Sjmatthew 
6204cd89cf2Sjmatthew 	sc->sc_mbox[0] = QLA_MBOX_INIT_FIRMWARE;
6214cd89cf2Sjmatthew 	sc->sc_mbox[4] = 0;
6224cd89cf2Sjmatthew 	sc->sc_mbox[5] = 0;
6234cd89cf2Sjmatthew 	qla_mbox_putaddr(sc->sc_mbox, sc->sc_scratch);
6244cd89cf2Sjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
6254cd89cf2Sjmatthew 	    sizeof(*icb), BUS_DMASYNC_PREWRITE);
62686376500Sjmatthew 	rv = qla_mbox(sc, 0x00fd);
6274cd89cf2Sjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
6284cd89cf2Sjmatthew 	    sizeof(*icb), BUS_DMASYNC_POSTWRITE);
6294cd89cf2Sjmatthew 
6304cd89cf2Sjmatthew 	if (rv != 0) {
6314cd89cf2Sjmatthew 		printf("%s: ISP firmware init failed: %x\n", DEVNAME(sc),
6324cd89cf2Sjmatthew 		    sc->sc_mbox[0]);
6334cd89cf2Sjmatthew 		goto free_scratch;
6344cd89cf2Sjmatthew 	}
6354cd89cf2Sjmatthew 
6364cd89cf2Sjmatthew 	/* enable some more notifications */
6374cd89cf2Sjmatthew 	sc->sc_mbox[0] = QLA_MBOX_SET_FIRMWARE_OPTIONS;
6384cd89cf2Sjmatthew 	sc->sc_mbox[1] = QLA_FW_OPTION1_ASYNC_LIP_F8 |
6394cd89cf2Sjmatthew 	    QLA_FW_OPTION1_ASYNC_LIP_RESET |
6404cd89cf2Sjmatthew 	    QLA_FW_OPTION1_ASYNC_LIP_ERROR |
6414cd89cf2Sjmatthew 	    QLA_FW_OPTION1_ASYNC_LOGIN_RJT;
6424cd89cf2Sjmatthew 	sc->sc_mbox[2] = 0;
6434cd89cf2Sjmatthew 	sc->sc_mbox[3] = 0;
64486376500Sjmatthew 	if (qla_mbox(sc, 0x000f)) {
6454cd89cf2Sjmatthew 		printf("%s: setting firmware options failed: %x\n",
6464cd89cf2Sjmatthew 		    DEVNAME(sc), sc->sc_mbox[0]);
6474cd89cf2Sjmatthew 		goto free_scratch;
6484cd89cf2Sjmatthew 	}
6494cd89cf2Sjmatthew 
65079ea9c08Sdlg 	sc->sc_update_taskq = taskq_create(DEVNAME(sc), 1, IPL_BIO, 0);
651e4195480Sdlg 	task_set(&sc->sc_update_task, qla_do_update, sc);
652944e332aSjmatthew 
6534cd89cf2Sjmatthew 	/* wait a bit for link to come up so we can scan and attach devices */
6544cd89cf2Sjmatthew 	for (i = 0; i < QLA_WAIT_FOR_LOOP * 10000; i++) {
6554cd89cf2Sjmatthew 		u_int16_t isr, info;
6564cd89cf2Sjmatthew 
6574cd89cf2Sjmatthew 		delay(100);
6584cd89cf2Sjmatthew 
6594cd89cf2Sjmatthew 		if (qla_read_isr(sc, &isr, &info) == 0)
6604cd89cf2Sjmatthew 			continue;
6614cd89cf2Sjmatthew 
6624cd89cf2Sjmatthew 		qla_handle_intr(sc, isr, info);
6634cd89cf2Sjmatthew 
6644cd89cf2Sjmatthew 		if (sc->sc_loop_up)
6654cd89cf2Sjmatthew 			break;
6664cd89cf2Sjmatthew 	}
6674cd89cf2Sjmatthew 
668aa14606aSjmatthew 	if (sc->sc_loop_up) {
669e4195480Sdlg 		qla_do_update(sc);
6704cd89cf2Sjmatthew 	} else {
671f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: loop still down, giving up\n",
672f901d880Sjmatthew 		    DEVNAME(sc));
6734cd89cf2Sjmatthew 	}
6744cd89cf2Sjmatthew 
675ead808c4Skrw 	saa.saa_adapter = &qla_switch;
676ead808c4Skrw 	saa.saa_adapter_softc = sc;
677ead808c4Skrw 	if (sc->sc_2k_logins) {
678ead808c4Skrw 		saa.saa_adapter_buswidth = QLA_2KL_BUSWIDTH;
679ead808c4Skrw 	} else {
680ead808c4Skrw 		saa.saa_adapter_buswidth = QLA_BUSWIDTH;
681ead808c4Skrw 	}
682ead808c4Skrw 	saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
683ead808c4Skrw 	saa.saa_luns = 8;
684e5eae15dSkrw 	saa.saa_openings = sc->sc_maxcmds;
685e5eae15dSkrw 	saa.saa_pool = &sc->sc_iopool;
686e5eae15dSkrw 	saa.saa_wwpn = sc->sc_port_name;
687e5eae15dSkrw 	saa.saa_wwnn = sc->sc_node_name;
688e5eae15dSkrw 	if (saa.saa_wwnn == 0) {
689e5eae15dSkrw 		/*
690e5eae15dSkrw 		 * mask out the port number from the port name to get
691e5eae15dSkrw 		 * the node name.
692e5eae15dSkrw 		 */
693e5eae15dSkrw 		saa.saa_wwnn = saa.saa_wwpn;
694e5eae15dSkrw 		saa.saa_wwnn &= ~(0xfULL << 56);
695e5eae15dSkrw 	}
696e5eae15dSkrw 	saa.saa_quirks = saa.saa_flags = 0;
6974cd89cf2Sjmatthew 
6984cd89cf2Sjmatthew 	sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev,
6994cd89cf2Sjmatthew 	    &saa, scsiprint);
7004cd89cf2Sjmatthew 
7014cd89cf2Sjmatthew 	return(0);
7024cd89cf2Sjmatthew 
7034cd89cf2Sjmatthew free_scratch:
7044cd89cf2Sjmatthew 	qla_dmamem_free(sc, sc->sc_scratch);
7054cd89cf2Sjmatthew free_ccbs:
7064cd89cf2Sjmatthew 	qla_free_ccbs(sc);
7074cd89cf2Sjmatthew 	return (ENXIO);
7084cd89cf2Sjmatthew }
7094cd89cf2Sjmatthew 
7104cd89cf2Sjmatthew int
qla_detach(struct qla_softc * sc,int flags)7114cd89cf2Sjmatthew qla_detach(struct qla_softc *sc, int flags)
7124cd89cf2Sjmatthew {
7134cd89cf2Sjmatthew 	return (0);
7144cd89cf2Sjmatthew }
7154cd89cf2Sjmatthew 
7164cd89cf2Sjmatthew struct qla_ccb *
qla_handle_resp(struct qla_softc * sc,u_int16_t id)7174cd89cf2Sjmatthew qla_handle_resp(struct qla_softc *sc, u_int16_t id)
7184cd89cf2Sjmatthew {
7194cd89cf2Sjmatthew 	struct qla_ccb *ccb;
7204cd89cf2Sjmatthew 	struct qla_iocb_status *status;
7214cd89cf2Sjmatthew 	struct scsi_xfer *xs;
7224cd89cf2Sjmatthew 	u_int32_t handle;
7234cd89cf2Sjmatthew 	u_int8_t *entry;
7244cd89cf2Sjmatthew 
7254cd89cf2Sjmatthew 	ccb = NULL;
7264cd89cf2Sjmatthew 	entry = QLA_DMA_KVA(sc->sc_responses) + (id * QLA_QUEUE_ENTRY_SIZE);
7274cd89cf2Sjmatthew 
7284cd89cf2Sjmatthew 	bus_dmamap_sync(sc->sc_dmat,
7294cd89cf2Sjmatthew 	    QLA_DMA_MAP(sc->sc_responses), id * QLA_QUEUE_ENTRY_SIZE,
7304cd89cf2Sjmatthew 	    QLA_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTREAD);
7314cd89cf2Sjmatthew 
732f901d880Sjmatthew 	qla_dump_iocb(sc, entry);
7334cd89cf2Sjmatthew 	switch (entry[0]) {
7344cd89cf2Sjmatthew 	case QLA_IOCB_STATUS:
7354cd89cf2Sjmatthew 		status = (struct qla_iocb_status *)entry;
7364cd89cf2Sjmatthew 		handle = status->handle;
7374cd89cf2Sjmatthew 		if (handle > sc->sc_maxcmds) {
7384cd89cf2Sjmatthew 			panic("bad completed command handle: %d (> %d)",
7394cd89cf2Sjmatthew 			    handle, sc->sc_maxcmds);
7404cd89cf2Sjmatthew 		}
7414cd89cf2Sjmatthew 
7424cd89cf2Sjmatthew 		ccb = &sc->sc_ccbs[handle];
7434cd89cf2Sjmatthew 		xs = ccb->ccb_xs;
7444cd89cf2Sjmatthew 		if (xs == NULL) {
745f901d880Sjmatthew 			DPRINTF(QLA_D_INTR, "%s: got status for inactive"
746f901d880Sjmatthew 			    " ccb %d\n", DEVNAME(sc), handle);
7474cd89cf2Sjmatthew 			ccb = NULL;
7484cd89cf2Sjmatthew 			break;
7494cd89cf2Sjmatthew 		}
7504cd89cf2Sjmatthew 		if (xs->io != ccb) {
7514cd89cf2Sjmatthew 			panic("completed command handle doesn't match xs "
7524cd89cf2Sjmatthew 			    "(handle %d, ccb %p, xs->io %p)", handle, ccb,
7534cd89cf2Sjmatthew 			    xs->io);
7544cd89cf2Sjmatthew 		}
7554cd89cf2Sjmatthew 
7564cd89cf2Sjmatthew 		if (xs->datalen > 0) {
7574cd89cf2Sjmatthew 			if (ccb->ccb_dmamap->dm_nsegs >
7584cd89cf2Sjmatthew 			    QLA_IOCB_SEGS_PER_CMD) {
7594cd89cf2Sjmatthew 				bus_dmamap_sync(sc->sc_dmat,
7604cd89cf2Sjmatthew 				    QLA_DMA_MAP(sc->sc_segments),
7614cd89cf2Sjmatthew 				    ccb->ccb_seg_offset,
7624cd89cf2Sjmatthew 				    sizeof(*ccb->ccb_t4segs) *
7634cd89cf2Sjmatthew 				    ccb->ccb_dmamap->dm_nsegs,
7644cd89cf2Sjmatthew 				    BUS_DMASYNC_POSTWRITE);
7654cd89cf2Sjmatthew 			}
7664cd89cf2Sjmatthew 
7674cd89cf2Sjmatthew 			bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0,
7684cd89cf2Sjmatthew 			    ccb->ccb_dmamap->dm_mapsize,
7694cd89cf2Sjmatthew 			    (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD :
7704cd89cf2Sjmatthew 			    BUS_DMASYNC_POSTWRITE);
7714cd89cf2Sjmatthew 			bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap);
7724cd89cf2Sjmatthew 		}
7734cd89cf2Sjmatthew 
7744cd89cf2Sjmatthew 		xs->status = letoh16(status->scsi_status);
7754cd89cf2Sjmatthew 		switch (letoh16(status->completion)) {
7764cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_COMPLETE:
7774cd89cf2Sjmatthew 			if (letoh16(status->scsi_status) &
7784cd89cf2Sjmatthew 			    QLA_SCSI_STATUS_SENSE_VALID) {
7794cd89cf2Sjmatthew 				memcpy(&xs->sense, status->sense_data,
7804cd89cf2Sjmatthew 				    sizeof(xs->sense));
7814cd89cf2Sjmatthew 				xs->error = XS_SENSE;
7824cd89cf2Sjmatthew 			} else {
7834cd89cf2Sjmatthew 				xs->error = XS_NOERROR;
7844cd89cf2Sjmatthew 			}
7854cd89cf2Sjmatthew 			xs->resid = 0;
7864cd89cf2Sjmatthew 			break;
7874cd89cf2Sjmatthew 
7884cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_DMA_ERROR:
789f901d880Sjmatthew 			DPRINTF(QLA_D_INTR, "%s: dma error\n", DEVNAME(sc));
7904cd89cf2Sjmatthew 			/* set resid apparently? */
7914cd89cf2Sjmatthew 			break;
7924cd89cf2Sjmatthew 
7934cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_RESET:
794f901d880Sjmatthew 			DPRINTF(QLA_D_IO, "%s: reset destroyed command\n",
795f901d880Sjmatthew 			    DEVNAME(sc));
7964cd89cf2Sjmatthew 			sc->sc_marker_required = 1;
7974cd89cf2Sjmatthew 			xs->error = XS_RESET;
7984cd89cf2Sjmatthew 			break;
7994cd89cf2Sjmatthew 
8004cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_ABORTED:
801f901d880Sjmatthew 			DPRINTF(QLA_D_IO, "%s: aborted\n", DEVNAME(sc));
8024cd89cf2Sjmatthew 			sc->sc_marker_required = 1;
8034cd89cf2Sjmatthew 			xs->error = XS_DRIVER_STUFFUP;
8044cd89cf2Sjmatthew 			break;
8054cd89cf2Sjmatthew 
8064cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_TIMEOUT:
807f901d880Sjmatthew 			DPRINTF(QLA_D_IO, "%s: command timed out\n",
808f901d880Sjmatthew 			    DEVNAME(sc));
8094cd89cf2Sjmatthew 			xs->error = XS_TIMEOUT;
8104cd89cf2Sjmatthew 			break;
8114cd89cf2Sjmatthew 
8124cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_DATA_OVERRUN:
8134cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_DATA_UNDERRUN:
8144cd89cf2Sjmatthew 			xs->resid = letoh32(status->resid);
8154cd89cf2Sjmatthew 			xs->error = XS_NOERROR;
8164cd89cf2Sjmatthew 			break;
8174cd89cf2Sjmatthew 
8184cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_QUEUE_FULL:
819f901d880Sjmatthew 			DPRINTF(QLA_D_IO, "%s: queue full\n", DEVNAME(sc));
8204cd89cf2Sjmatthew 			xs->error = XS_BUSY;
8214cd89cf2Sjmatthew 			break;
8224cd89cf2Sjmatthew 
8234cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_PORT_UNAVAIL:
8244cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_PORT_LOGGED_OUT:
8254cd89cf2Sjmatthew 		case QLA_IOCB_STATUS_PORT_CHANGED:
826f901d880Sjmatthew 			DPRINTF(QLA_D_IO, "%s: dev gone\n", DEVNAME(sc));
8274cd89cf2Sjmatthew 			xs->error = XS_SELTIMEOUT;
8284cd89cf2Sjmatthew 			break;
8294cd89cf2Sjmatthew 
8304cd89cf2Sjmatthew 		default:
831f901d880Sjmatthew 			DPRINTF(QLA_D_INTR, "%s: unexpected completion"
832f901d880Sjmatthew 			    " status %x\n", DEVNAME(sc), status->completion);
8334cd89cf2Sjmatthew 			xs->error = XS_DRIVER_STUFFUP;
8344cd89cf2Sjmatthew 			break;
8354cd89cf2Sjmatthew 		}
8364cd89cf2Sjmatthew 		break;
8374cd89cf2Sjmatthew 
8384cd89cf2Sjmatthew 	case QLA_IOCB_STATUS_CONT:
839f901d880Sjmatthew 		DPRINTF(QLA_D_INTR, "%s: ignoring status continuation iocb\n",
8404cd89cf2Sjmatthew 		    DEVNAME(sc));
8414cd89cf2Sjmatthew 		break;
8424cd89cf2Sjmatthew 
8434cd89cf2Sjmatthew 		/* check for requests that bounce back? */
8444cd89cf2Sjmatthew 	default:
845f901d880Sjmatthew 		DPRINTF(QLA_D_INTR, "%s: unexpected response entry type %x\n",
8464cd89cf2Sjmatthew 		    DEVNAME(sc), entry[0]);
8474cd89cf2Sjmatthew 		break;
8484cd89cf2Sjmatthew 	}
8494cd89cf2Sjmatthew 
8504cd89cf2Sjmatthew 	return (ccb);
8514cd89cf2Sjmatthew }
8524cd89cf2Sjmatthew 
85385121d75Sjmatthew void
qla_handle_intr(struct qla_softc * sc,u_int16_t isr,u_int16_t info)8544cd89cf2Sjmatthew qla_handle_intr(struct qla_softc *sc, u_int16_t isr, u_int16_t info)
8554cd89cf2Sjmatthew {
85685121d75Sjmatthew 	int i;
8574cd89cf2Sjmatthew 	u_int16_t rspin;
8584cd89cf2Sjmatthew 	struct qla_ccb *ccb;
8594cd89cf2Sjmatthew 
8604cd89cf2Sjmatthew 	switch (isr) {
861023e4a3eSjmatthew 	case QLA_INT_TYPE_ASYNC:
86285121d75Sjmatthew 		qla_async(sc, info);
8634cd89cf2Sjmatthew 		break;
8644cd89cf2Sjmatthew 
865023e4a3eSjmatthew 	case QLA_INT_TYPE_IO:
86668858f63Sdlg 		rspin = qla_queue_read(sc, sc->sc_regs->res_in);
8674cd89cf2Sjmatthew 		if (rspin == sc->sc_last_resp_id) {
868db1d60ccSjmatthew 			/* seems to happen a lot on 2200s when mbox commands
869db1d60ccSjmatthew 			 * complete but it doesn't want to give us the register
870db1d60ccSjmatthew 			 * semaphore, or something.
871db1d60ccSjmatthew 			 *
872db1d60ccSjmatthew 			 * if we're waiting on a mailbox command, don't ack
873db1d60ccSjmatthew 			 * the interrupt yet.
874db1d60ccSjmatthew 			 */
875db1d60ccSjmatthew 			if (sc->sc_mbox_pending) {
876f901d880Sjmatthew 				DPRINTF(QLA_D_MBOX, "%s: ignoring premature"
877f901d880Sjmatthew 				    " mbox int\n", DEVNAME(sc));
878db1d60ccSjmatthew 				return;
879db1d60ccSjmatthew 			}
880db1d60ccSjmatthew 
881b6400b02Sdlg 			break;
882b6400b02Sdlg 		}
883b6400b02Sdlg 
8849230beccSkettenis 		if (sc->sc_responses == NULL)
8859230beccSkettenis 			break;
8869230beccSkettenis 
887f901d880Sjmatthew 		DPRINTF(QLA_D_IO, "%s: response queue %x=>%x\n",
888f901d880Sjmatthew 		    DEVNAME(sc), sc->sc_last_resp_id, rspin);
889b6400b02Sdlg 
890b6400b02Sdlg 		do {
8914cd89cf2Sjmatthew 			ccb = qla_handle_resp(sc, sc->sc_last_resp_id);
8924cd89cf2Sjmatthew 			if (ccb)
8934cd89cf2Sjmatthew 				scsi_done(ccb->ccb_xs);
8944cd89cf2Sjmatthew 
8954cd89cf2Sjmatthew 			sc->sc_last_resp_id++;
896b6400b02Sdlg 			sc->sc_last_resp_id %= sc->sc_maxcmds;
897b6400b02Sdlg 		} while (sc->sc_last_resp_id != rspin);
8984cd89cf2Sjmatthew 
89968858f63Sdlg 		qla_queue_write(sc, sc->sc_regs->res_out, rspin);
9004cd89cf2Sjmatthew 		break;
9014cd89cf2Sjmatthew 
902023e4a3eSjmatthew 	case QLA_INT_TYPE_MBOX:
90386376500Sjmatthew 		mtx_enter(&sc->sc_mbox_mtx);
9044cd89cf2Sjmatthew 		if (sc->sc_mbox_pending) {
90586376500Sjmatthew 			DPRINTF(QLA_D_MBOX, "%s: mbox response %x\n",
90686376500Sjmatthew 			    DEVNAME(sc), info);
90786376500Sjmatthew 			for (i = 0; i < nitems(sc->sc_mbox); i++) {
9084cd89cf2Sjmatthew 				sc->sc_mbox[i] = qla_read_mbox(sc, i);
9094cd89cf2Sjmatthew 			}
91086376500Sjmatthew 			sc->sc_mbox_pending = 2;
9114cd89cf2Sjmatthew 			wakeup(sc->sc_mbox);
91286376500Sjmatthew 			mtx_leave(&sc->sc_mbox_mtx);
9134cd89cf2Sjmatthew 		} else {
91486376500Sjmatthew 			mtx_leave(&sc->sc_mbox_mtx);
915f901d880Sjmatthew 			DPRINTF(QLA_D_MBOX, "%s: unexpected mbox interrupt:"
916f901d880Sjmatthew 			    " %x\n", DEVNAME(sc), info);
9174cd89cf2Sjmatthew 		}
9184cd89cf2Sjmatthew 		break;
9194cd89cf2Sjmatthew 
9204cd89cf2Sjmatthew 	default:
9214cd89cf2Sjmatthew 		/* maybe log something? */
92285121d75Sjmatthew 		break;
9234cd89cf2Sjmatthew 	}
9244cd89cf2Sjmatthew 
925af65b7a1Sjmatthew 	qla_clear_isr(sc, isr);
9264cd89cf2Sjmatthew }
9274cd89cf2Sjmatthew 
9284cd89cf2Sjmatthew int
qla_intr(void * xsc)9294cd89cf2Sjmatthew qla_intr(void *xsc)
9304cd89cf2Sjmatthew {
9314cd89cf2Sjmatthew 	struct qla_softc *sc = xsc;
9324cd89cf2Sjmatthew 	u_int16_t isr;
9334cd89cf2Sjmatthew 	u_int16_t info;
9344cd89cf2Sjmatthew 
9354cd89cf2Sjmatthew 	if (qla_read_isr(sc, &isr, &info) == 0)
9364cd89cf2Sjmatthew 		return (0);
9374cd89cf2Sjmatthew 
93885121d75Sjmatthew 	qla_handle_intr(sc, isr, info);
93985121d75Sjmatthew 	return (1);
9404cd89cf2Sjmatthew }
9414cd89cf2Sjmatthew 
9424cd89cf2Sjmatthew int
qla_scsi_probe(struct scsi_link * link)9434cd89cf2Sjmatthew qla_scsi_probe(struct scsi_link *link)
9444cd89cf2Sjmatthew {
9450b29cb40Skrw 	struct qla_softc *sc = link->bus->sb_adapter_softc;
9464cd89cf2Sjmatthew 	int rv = 0;
9474cd89cf2Sjmatthew 
9484cd89cf2Sjmatthew 	mtx_enter(&sc->sc_port_mtx);
9494cd89cf2Sjmatthew 	if (sc->sc_targets[link->target] == NULL)
9504cd89cf2Sjmatthew 		rv = ENXIO;
9514cd89cf2Sjmatthew 	else if (!ISSET(sc->sc_targets[link->target]->flags,
9524cd89cf2Sjmatthew 	    QLA_PORT_FLAG_IS_TARGET))
9534cd89cf2Sjmatthew 		rv = ENXIO;
954662c3a58Skettenis 	else {
955662c3a58Skettenis 		link->port_wwn = sc->sc_targets[link->target]->port_name;
956662c3a58Skettenis 		link->node_wwn = sc->sc_targets[link->target]->node_name;
957662c3a58Skettenis 	}
9584cd89cf2Sjmatthew 	mtx_leave(&sc->sc_port_mtx);
9594cd89cf2Sjmatthew 
9604cd89cf2Sjmatthew 	return (rv);
9614cd89cf2Sjmatthew }
9624cd89cf2Sjmatthew 
9634cd89cf2Sjmatthew void
qla_scsi_cmd(struct scsi_xfer * xs)9644cd89cf2Sjmatthew qla_scsi_cmd(struct scsi_xfer *xs)
9654cd89cf2Sjmatthew {
9664cd89cf2Sjmatthew 	struct scsi_link	*link = xs->sc_link;
9670b29cb40Skrw 	struct qla_softc	*sc = link->bus->sb_adapter_softc;
9684cd89cf2Sjmatthew 	struct qla_ccb		*ccb;
9694cd89cf2Sjmatthew 	struct qla_iocb_req34	*iocb;
9704cd89cf2Sjmatthew 	struct qla_ccb_list	list;
971d6fdc34dSjmatthew 	u_int16_t		req, rspin;
972d6fdc34dSjmatthew 	int			offset, error, done;
9734cd89cf2Sjmatthew 	bus_dmamap_t		dmap;
9744cd89cf2Sjmatthew 
9754cd89cf2Sjmatthew 	if (xs->cmdlen > sizeof(iocb->req_cdb)) {
976f901d880Sjmatthew 		DPRINTF(QLA_D_IO, "%s: cdb too big (%d)\n", DEVNAME(sc),
977f901d880Sjmatthew 		    xs->cmdlen);
9784cd89cf2Sjmatthew 		memset(&xs->sense, 0, sizeof(xs->sense));
9794cd89cf2Sjmatthew 		xs->sense.error_code = SSD_ERRCODE_VALID | SSD_ERRCODE_CURRENT;
9804cd89cf2Sjmatthew 		xs->sense.flags = SKEY_ILLEGAL_REQUEST;
9814cd89cf2Sjmatthew 		xs->sense.add_sense_code = 0x20;
9824cd89cf2Sjmatthew 		xs->error = XS_SENSE;
9834cd89cf2Sjmatthew 		scsi_done(xs);
9844cd89cf2Sjmatthew 		return;
9854cd89cf2Sjmatthew 	}
9864cd89cf2Sjmatthew 
9874cd89cf2Sjmatthew 	ccb = xs->io;
9884cd89cf2Sjmatthew 	dmap = ccb->ccb_dmamap;
9894cd89cf2Sjmatthew 	if (xs->datalen > 0) {
9904cd89cf2Sjmatthew 		error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data,
9914cd89cf2Sjmatthew 		    xs->datalen, NULL, (xs->flags & SCSI_NOSLEEP) ?
9924cd89cf2Sjmatthew 		    BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
9934cd89cf2Sjmatthew 		if (error) {
9944cd89cf2Sjmatthew 			xs->error = XS_DRIVER_STUFFUP;
9954cd89cf2Sjmatthew 			scsi_done(xs);
9964cd89cf2Sjmatthew 			return;
9974cd89cf2Sjmatthew 		}
9984cd89cf2Sjmatthew 
9994cd89cf2Sjmatthew 		bus_dmamap_sync(sc->sc_dmat, dmap, 0,
10004cd89cf2Sjmatthew 		    dmap->dm_mapsize,
10014cd89cf2Sjmatthew 		    (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
10024cd89cf2Sjmatthew 		    BUS_DMASYNC_PREWRITE);
10034cd89cf2Sjmatthew 	}
10044cd89cf2Sjmatthew 
10054cd89cf2Sjmatthew 	mtx_enter(&sc->sc_queue_mtx);
10064cd89cf2Sjmatthew 
10074cd89cf2Sjmatthew 	/* put in a sync marker if required */
10084cd89cf2Sjmatthew 	if (sc->sc_marker_required) {
10094cd89cf2Sjmatthew 		req = sc->sc_next_req_id++;
10104cd89cf2Sjmatthew 		if (sc->sc_next_req_id == sc->sc_maxcmds)
10114cd89cf2Sjmatthew 			sc->sc_next_req_id = 0;
10124cd89cf2Sjmatthew 
1013f901d880Sjmatthew 		DPRINTF(QLA_D_IO, "%s: writing marker at request %d\n",
1014f901d880Sjmatthew 		    DEVNAME(sc), req);
10154cd89cf2Sjmatthew 		offset = (req * QLA_QUEUE_ENTRY_SIZE);
10164cd89cf2Sjmatthew 		iocb = QLA_DMA_KVA(sc->sc_requests) + offset;
10174cd89cf2Sjmatthew 		bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_requests),
10184cd89cf2Sjmatthew 		    offset, QLA_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE);
10194cd89cf2Sjmatthew 		qla_put_marker(sc, iocb);
102068858f63Sdlg 		qla_queue_write(sc, sc->sc_regs->req_in, sc->sc_next_req_id);
10214cd89cf2Sjmatthew 		sc->sc_marker_required = 0;
10224cd89cf2Sjmatthew 	}
10234cd89cf2Sjmatthew 
10244cd89cf2Sjmatthew 	req = sc->sc_next_req_id++;
10254cd89cf2Sjmatthew 	if (sc->sc_next_req_id == sc->sc_maxcmds)
10264cd89cf2Sjmatthew 		sc->sc_next_req_id = 0;
10274cd89cf2Sjmatthew 
10284cd89cf2Sjmatthew 	offset = (req * QLA_QUEUE_ENTRY_SIZE);
10294cd89cf2Sjmatthew 	iocb = QLA_DMA_KVA(sc->sc_requests) + offset;
10304cd89cf2Sjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_requests), offset,
10314cd89cf2Sjmatthew 	    QLA_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE);
10324cd89cf2Sjmatthew 
10334cd89cf2Sjmatthew 	ccb->ccb_xs = xs;
10344cd89cf2Sjmatthew 
1035f901d880Sjmatthew 	DPRINTF(QLA_D_IO, "%s: writing cmd at request %d\n", DEVNAME(sc), req);
10364cd89cf2Sjmatthew 	qla_put_cmd(sc, iocb, xs, ccb);
10374cd89cf2Sjmatthew 
103868858f63Sdlg 	qla_queue_write(sc, sc->sc_regs->req_in, sc->sc_next_req_id);
10394cd89cf2Sjmatthew 
10404cd89cf2Sjmatthew 	if (!ISSET(xs->flags, SCSI_POLL)) {
10414cd89cf2Sjmatthew 		mtx_leave(&sc->sc_queue_mtx);
10424cd89cf2Sjmatthew 		return;
10434cd89cf2Sjmatthew 	}
10444cd89cf2Sjmatthew 
1045d6fdc34dSjmatthew 	done = 0;
10464cd89cf2Sjmatthew 	SIMPLEQ_INIT(&list);
10474cd89cf2Sjmatthew 	do {
10484cd89cf2Sjmatthew 		u_int16_t isr, info;
10494cd89cf2Sjmatthew 
1050498578c4Skettenis 		delay(100);
1051498578c4Skettenis 
10524cd89cf2Sjmatthew 		if (qla_read_isr(sc, &isr, &info) == 0) {
10534cd89cf2Sjmatthew 			continue;
10544cd89cf2Sjmatthew 		}
10554cd89cf2Sjmatthew 
1056023e4a3eSjmatthew 		if (isr != QLA_INT_TYPE_IO) {
10574cd89cf2Sjmatthew 			qla_handle_intr(sc, isr, info);
10584cd89cf2Sjmatthew 			continue;
10594cd89cf2Sjmatthew 		}
10604cd89cf2Sjmatthew 
106168858f63Sdlg 		rspin = qla_queue_read(sc, sc->sc_regs->res_in);
1062d6fdc34dSjmatthew 		while (rspin != sc->sc_last_resp_id) {
10634cd89cf2Sjmatthew 			ccb = qla_handle_resp(sc, sc->sc_last_resp_id);
10644cd89cf2Sjmatthew 
10654cd89cf2Sjmatthew 			sc->sc_last_resp_id++;
10664cd89cf2Sjmatthew 			if (sc->sc_last_resp_id == sc->sc_maxcmds)
10674cd89cf2Sjmatthew 				sc->sc_last_resp_id = 0;
10684cd89cf2Sjmatthew 
1069d6fdc34dSjmatthew 			if (ccb != NULL)
1070d6fdc34dSjmatthew 				SIMPLEQ_INSERT_TAIL(&list, ccb, ccb_link);
1071d6fdc34dSjmatthew 			if (ccb == xs->io)
1072d6fdc34dSjmatthew 				done = 1;
10734cd89cf2Sjmatthew 		}
1074d6fdc34dSjmatthew 		qla_queue_write(sc, sc->sc_regs->res_out, rspin);
1075af65b7a1Sjmatthew 		qla_clear_isr(sc, isr);
1076d6fdc34dSjmatthew 	} while (done == 0);
10774cd89cf2Sjmatthew 
1078d6fdc34dSjmatthew 	mtx_leave(&sc->sc_queue_mtx);
1079d6fdc34dSjmatthew 
1080d6fdc34dSjmatthew 	while ((ccb = SIMPLEQ_FIRST(&list)) != NULL) {
1081d6fdc34dSjmatthew 		SIMPLEQ_REMOVE_HEAD(&list, ccb_link);
1082d6fdc34dSjmatthew 		scsi_done(ccb->ccb_xs);
1083d6fdc34dSjmatthew 	}
10844cd89cf2Sjmatthew }
10854cd89cf2Sjmatthew 
10864cd89cf2Sjmatthew u_int16_t
qla_read(struct qla_softc * sc,bus_size_t offset)1087b6400b02Sdlg qla_read(struct qla_softc *sc, bus_size_t offset)
10884cd89cf2Sjmatthew {
10894cd89cf2Sjmatthew 	u_int16_t v;
10904cd89cf2Sjmatthew 	v = bus_space_read_2(sc->sc_iot, sc->sc_ioh, offset);
10914cd89cf2Sjmatthew 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, offset, 2,
10924cd89cf2Sjmatthew 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
10934cd89cf2Sjmatthew 	return (v);
10944cd89cf2Sjmatthew }
10954cd89cf2Sjmatthew 
10964cd89cf2Sjmatthew void
qla_write(struct qla_softc * sc,bus_size_t offset,u_int16_t value)1097b6400b02Sdlg qla_write(struct qla_softc *sc, bus_size_t offset, u_int16_t value)
10984cd89cf2Sjmatthew {
10994cd89cf2Sjmatthew 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, offset, value);
11004cd89cf2Sjmatthew 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, offset, 2,
11014cd89cf2Sjmatthew 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
11024cd89cf2Sjmatthew }
11034cd89cf2Sjmatthew 
11044cd89cf2Sjmatthew u_int16_t
qla_read_mbox(struct qla_softc * sc,int mbox)11054cd89cf2Sjmatthew qla_read_mbox(struct qla_softc *sc, int mbox)
11064cd89cf2Sjmatthew {
11074cd89cf2Sjmatthew 	/* could range-check mboxes according to chip type? */
11084cd89cf2Sjmatthew 	return (qla_read(sc, sc->sc_mbox_base + (mbox * 2)));
11094cd89cf2Sjmatthew }
11104cd89cf2Sjmatthew 
11114cd89cf2Sjmatthew void
qla_write_mbox(struct qla_softc * sc,int mbox,u_int16_t value)11124cd89cf2Sjmatthew qla_write_mbox(struct qla_softc *sc, int mbox, u_int16_t value)
11134cd89cf2Sjmatthew {
11144cd89cf2Sjmatthew 	qla_write(sc, sc->sc_mbox_base + (mbox * 2), value);
11154cd89cf2Sjmatthew }
11164cd89cf2Sjmatthew 
11174cd89cf2Sjmatthew void
qla_host_cmd(struct qla_softc * sc,u_int16_t cmd)11184cd89cf2Sjmatthew qla_host_cmd(struct qla_softc *sc, u_int16_t cmd)
11194cd89cf2Sjmatthew {
11204cd89cf2Sjmatthew 	qla_write(sc, QLA_HOST_CMD_CTRL, cmd << QLA_HOST_CMD_SHIFT);
11214cd89cf2Sjmatthew }
11224cd89cf2Sjmatthew 
11234cd89cf2Sjmatthew #define MBOX_COMMAND_TIMEOUT	4000
11244cd89cf2Sjmatthew 
11254cd89cf2Sjmatthew int
qla_mbox(struct qla_softc * sc,int maskin)112686376500Sjmatthew qla_mbox(struct qla_softc *sc, int maskin)
11274cd89cf2Sjmatthew {
11284cd89cf2Sjmatthew 	int i;
11294cd89cf2Sjmatthew 	int result = 0;
11304cd89cf2Sjmatthew 	int rv;
11314cd89cf2Sjmatthew 
1132db1d60ccSjmatthew 	sc->sc_mbox_pending = 1;
11334cd89cf2Sjmatthew 	for (i = 0; i < nitems(sc->sc_mbox); i++) {
11344cd89cf2Sjmatthew 		if (maskin & (1 << i)) {
11354cd89cf2Sjmatthew 			qla_write_mbox(sc, i, sc->sc_mbox[i]);
11364cd89cf2Sjmatthew 		}
11374cd89cf2Sjmatthew 	}
11384cd89cf2Sjmatthew 	qla_host_cmd(sc, QLA_HOST_CMD_SET_HOST_INT);
11394cd89cf2Sjmatthew 
114086376500Sjmatthew 	if (sc->sc_scsibus != NULL) {
114186376500Sjmatthew 		mtx_enter(&sc->sc_mbox_mtx);
114286376500Sjmatthew 		sc->sc_mbox_pending = 1;
114386376500Sjmatthew 		while (sc->sc_mbox_pending == 1) {
1144541245b6Sjsg 			msleep_nsec(sc->sc_mbox, &sc->sc_mbox_mtx, PRIBIO,
1145541245b6Sjsg 			    "qlambox", INFSLP);
114686376500Sjmatthew 		}
114786376500Sjmatthew 		result = sc->sc_mbox[0];
114886376500Sjmatthew 		sc->sc_mbox_pending = 0;
114986376500Sjmatthew 		mtx_leave(&sc->sc_mbox_mtx);
115086376500Sjmatthew 		return (result == QLA_MBOX_COMPLETE ? 0 : result);
115186376500Sjmatthew 	}
115286376500Sjmatthew 
11534cd89cf2Sjmatthew 	for (i = 0; i < MBOX_COMMAND_TIMEOUT && result == 0; i++) {
11544cd89cf2Sjmatthew 		u_int16_t isr, info;
11554cd89cf2Sjmatthew 
11564cd89cf2Sjmatthew 		delay(100);
11574cd89cf2Sjmatthew 
11584cd89cf2Sjmatthew 		if (qla_read_isr(sc, &isr, &info) == 0)
11594cd89cf2Sjmatthew 			continue;
11604cd89cf2Sjmatthew 
11614cd89cf2Sjmatthew 		switch (isr) {
1162023e4a3eSjmatthew 		case QLA_INT_TYPE_MBOX:
11634cd89cf2Sjmatthew 			result = info;
11644cd89cf2Sjmatthew 			break;
11654cd89cf2Sjmatthew 
11664cd89cf2Sjmatthew 		default:
11674cd89cf2Sjmatthew 			qla_handle_intr(sc, isr, info);
11684cd89cf2Sjmatthew 			break;
11694cd89cf2Sjmatthew 		}
11704cd89cf2Sjmatthew 	}
11714cd89cf2Sjmatthew 
117286376500Sjmatthew 	if (result == 0) {
11734cd89cf2Sjmatthew 		/* timed out; do something? */
1174f901d880Sjmatthew 		DPRINTF(QLA_D_MBOX, "%s: mbox timed out\n", DEVNAME(sc));
11754cd89cf2Sjmatthew 		rv = 1;
117686376500Sjmatthew 	} else {
117786376500Sjmatthew 		for (i = 0; i < nitems(sc->sc_mbox); i++) {
117886376500Sjmatthew 			sc->sc_mbox[i] = qla_read_mbox(sc, i);
117986376500Sjmatthew 		}
118086376500Sjmatthew 		rv = (result == QLA_MBOX_COMPLETE ? 0 : result);
11814cd89cf2Sjmatthew 	}
11824cd89cf2Sjmatthew 
1183af65b7a1Sjmatthew 	qla_clear_isr(sc, QLA_INT_TYPE_MBOX);
1184db1d60ccSjmatthew 	sc->sc_mbox_pending = 0;
11854cd89cf2Sjmatthew 	return (rv);
11864cd89cf2Sjmatthew }
11874cd89cf2Sjmatthew 
11884cd89cf2Sjmatthew void
qla_mbox_putaddr(u_int16_t * mbox,struct qla_dmamem * mem)11894cd89cf2Sjmatthew qla_mbox_putaddr(u_int16_t *mbox, struct qla_dmamem *mem)
11904cd89cf2Sjmatthew {
11914cd89cf2Sjmatthew 	mbox[2] = (QLA_DMA_DVA(mem) >> 16) & 0xffff;
11924cd89cf2Sjmatthew 	mbox[3] = (QLA_DMA_DVA(mem) >> 0) & 0xffff;
11934cd89cf2Sjmatthew 	mbox[6] = (QLA_DMA_DVA(mem) >> 48) & 0xffff;
11944cd89cf2Sjmatthew 	mbox[7] = (QLA_DMA_DVA(mem) >> 32) & 0xffff;
11954cd89cf2Sjmatthew }
11964cd89cf2Sjmatthew 
11974cd89cf2Sjmatthew int
qla_sns_req(struct qla_softc * sc,struct qla_dmamem * mem,int reqsize)11984cd89cf2Sjmatthew qla_sns_req(struct qla_softc *sc, struct qla_dmamem *mem, int reqsize)
11994cd89cf2Sjmatthew {
12004cd89cf2Sjmatthew 	struct qla_sns_req_hdr *header;
120191637d79Sdlg 	uint64_t dva;
12024cd89cf2Sjmatthew 	int rv;
12034cd89cf2Sjmatthew 
12044cd89cf2Sjmatthew 	memset(&sc->sc_mbox, 0, sizeof(sc->sc_mbox));
12054cd89cf2Sjmatthew 	sc->sc_mbox[0] = QLA_MBOX_SEND_SNS;
12064cd89cf2Sjmatthew 	sc->sc_mbox[1] = reqsize / 2;
12074cd89cf2Sjmatthew 	qla_mbox_putaddr(sc->sc_mbox, mem);
12084cd89cf2Sjmatthew 
12094cd89cf2Sjmatthew 	header = QLA_DMA_KVA(mem);
121091637d79Sdlg 	htolem16(&header->resp_len, (QLA_DMA_LEN(mem) - reqsize) / 2);
121191637d79Sdlg 	dva = QLA_DMA_DVA(mem) + reqsize;
121291637d79Sdlg 	htolem32(&header->resp_addr_lo, dva);
121391637d79Sdlg 	htolem32(&header->resp_addr_hi, dva >> 32);
12144cd89cf2Sjmatthew 	header->subcmd_len = htole16((reqsize - sizeof(*header)) / 2);
12154cd89cf2Sjmatthew 
12164cd89cf2Sjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0, QLA_DMA_LEN(mem),
12174cd89cf2Sjmatthew 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
121886376500Sjmatthew 	rv = qla_mbox(sc, 0x00cf);
12194cd89cf2Sjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0, QLA_DMA_LEN(mem),
12204cd89cf2Sjmatthew 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
12214cd89cf2Sjmatthew 
12224cd89cf2Sjmatthew 	return (rv);
12234cd89cf2Sjmatthew }
12244cd89cf2Sjmatthew 
12254cd89cf2Sjmatthew void
qla_set_ints(struct qla_softc * sc,int enabled)12264cd89cf2Sjmatthew qla_set_ints(struct qla_softc *sc, int enabled)
12274cd89cf2Sjmatthew {
12284cd89cf2Sjmatthew 	u_int16_t v = enabled ? (QLA_INT_REQ | QLA_RISC_INT_REQ) : 0;
12294cd89cf2Sjmatthew 	qla_write(sc, QLA_INT_CTRL, v);
12304cd89cf2Sjmatthew }
12314cd89cf2Sjmatthew 
12324cd89cf2Sjmatthew int
qla_read_isr_1G(struct qla_softc * sc,u_int16_t * isr,u_int16_t * info)123368858f63Sdlg qla_read_isr_1G(struct qla_softc *sc, u_int16_t *isr, u_int16_t *info)
12344cd89cf2Sjmatthew {
1235db1d60ccSjmatthew 	u_int16_t int_status;
12364cd89cf2Sjmatthew 
1237db1d60ccSjmatthew 	if (qla_read(sc, QLA_SEMA) & QLA_SEMA_LOCK) {
1238db1d60ccSjmatthew 		*info = qla_read_mbox(sc, 0);
1239db1d60ccSjmatthew 		if (*info & QLA_MBOX_HAS_STATUS)
1240db1d60ccSjmatthew 			*isr = QLA_INT_TYPE_MBOX;
1241db1d60ccSjmatthew 		else
1242db1d60ccSjmatthew 			*isr = QLA_INT_TYPE_ASYNC;
1243db1d60ccSjmatthew 	} else {
124487cde8bcSjmatthew 		int_status = qla_read(sc, QLA_INT_STATUS);
124587cde8bcSjmatthew 		if ((int_status & QLA_INT_REQ) == 0)
124687cde8bcSjmatthew 			return (0);
124787cde8bcSjmatthew 
1248db1d60ccSjmatthew 		*isr = QLA_INT_TYPE_IO;
1249db1d60ccSjmatthew 	}
1250db1d60ccSjmatthew 
125168858f63Sdlg 	return (1);
125268858f63Sdlg }
125368858f63Sdlg 
125468858f63Sdlg int
qla_read_isr_2G(struct qla_softc * sc,u_int16_t * isr,u_int16_t * info)125568858f63Sdlg qla_read_isr_2G(struct qla_softc *sc, u_int16_t *isr, u_int16_t *info)
125668858f63Sdlg {
125768858f63Sdlg 	u_int32_t v;
125868858f63Sdlg 
1259db1d60ccSjmatthew 	if ((qla_read(sc, QLA_INT_STATUS) & QLA_INT_REQ) == 0)
1260db1d60ccSjmatthew 		return (0);
1261db1d60ccSjmatthew 
126268858f63Sdlg 	v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, QLA_RISC_STATUS_LOW);
1263db1d60ccSjmatthew 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, QLA_RISC_STATUS_LOW,
1264db1d60ccSjmatthew 	    4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
12654cd89cf2Sjmatthew 
1266023e4a3eSjmatthew 	switch (v & QLA_INT_STATUS_MASK) {
1267023e4a3eSjmatthew 	case QLA_23XX_INT_ROM_MBOX:
1268023e4a3eSjmatthew 	case QLA_23XX_INT_ROM_MBOX_FAIL:
1269023e4a3eSjmatthew 	case QLA_23XX_INT_MBOX:
1270023e4a3eSjmatthew 	case QLA_23XX_INT_MBOX_FAIL:
1271023e4a3eSjmatthew 		*isr = QLA_INT_TYPE_MBOX;
1272023e4a3eSjmatthew 		break;
1273023e4a3eSjmatthew 
1274023e4a3eSjmatthew 	case QLA_23XX_INT_ASYNC:
1275023e4a3eSjmatthew 		*isr = QLA_INT_TYPE_ASYNC;
1276023e4a3eSjmatthew 		break;
1277023e4a3eSjmatthew 
1278023e4a3eSjmatthew 	case QLA_23XX_INT_RSPQ:
1279023e4a3eSjmatthew 		*isr = QLA_INT_TYPE_IO;
1280023e4a3eSjmatthew 		break;
1281023e4a3eSjmatthew 
1282023e4a3eSjmatthew 	default:
1283023e4a3eSjmatthew 		*isr = QLA_INT_TYPE_OTHER;
1284023e4a3eSjmatthew 		break;
1285023e4a3eSjmatthew 	}
12864cd89cf2Sjmatthew 
12874cd89cf2Sjmatthew 	*info = (v >> QLA_INT_INFO_SHIFT);
1288db1d60ccSjmatthew 
128968858f63Sdlg 	return (1);
12904cd89cf2Sjmatthew }
12914cd89cf2Sjmatthew 
12924cd89cf2Sjmatthew void
qla_clear_isr(struct qla_softc * sc,u_int16_t isr)1293af65b7a1Sjmatthew qla_clear_isr(struct qla_softc *sc, u_int16_t isr)
12944cd89cf2Sjmatthew {
12954cd89cf2Sjmatthew 	qla_host_cmd(sc, QLA_HOST_CMD_CLR_RISC_INT);
1296af65b7a1Sjmatthew 	switch (isr) {
1297af65b7a1Sjmatthew 	case QLA_INT_TYPE_MBOX:
1298af65b7a1Sjmatthew 	case QLA_INT_TYPE_ASYNC:
1299af65b7a1Sjmatthew 		qla_write(sc, QLA_SEMA, 0);
1300af65b7a1Sjmatthew 		break;
1301af65b7a1Sjmatthew 	default:
1302af65b7a1Sjmatthew 		break;
1303af65b7a1Sjmatthew 	}
13044cd89cf2Sjmatthew }
13054cd89cf2Sjmatthew 
13064cd89cf2Sjmatthew u_int16_t
qla_read_queue_2100(struct qla_softc * sc,bus_size_t queue)1307b6400b02Sdlg qla_read_queue_2100(struct qla_softc *sc, bus_size_t queue)
13084cd89cf2Sjmatthew {
13099eb00056Sjmatthew 	u_int16_t a, b, i;
1310b6400b02Sdlg 
1311e4f53fc2Sdlg 	for (i = 0; i < 1000; i++) {
1312b6400b02Sdlg 		a = qla_read(sc, queue);
1313b6400b02Sdlg 		b = qla_read(sc, queue);
1314b6400b02Sdlg 
1315b6400b02Sdlg 		if (a == b)
13169eb00056Sjmatthew 			return (a);
1317e4f53fc2Sdlg 	}
13189eb00056Sjmatthew 
1319e4f53fc2Sdlg 	DPRINTF(QLA_D_INTR, "%s: queue ptr unstable\n", DEVNAME(sc));
1320b6400b02Sdlg 
1321b6400b02Sdlg 	return (a);
13224cd89cf2Sjmatthew }
13234cd89cf2Sjmatthew 
1324aa14606aSjmatthew int
qla_softreset(struct qla_softc * sc)1325aa14606aSjmatthew qla_softreset(struct qla_softc *sc)
1326aa14606aSjmatthew {
1327aa14606aSjmatthew 	int i;
1328aa14606aSjmatthew 	qla_set_ints(sc, 0);
1329aa14606aSjmatthew 
1330aa14606aSjmatthew 	/* reset */
1331aa14606aSjmatthew 	qla_write(sc, QLA_CTRL_STATUS, QLA_CTRL_RESET);
1332aa14606aSjmatthew 	delay(100);
1333aa14606aSjmatthew 	/* clear data and control dma engines? */
1334aa14606aSjmatthew 
1335aa14606aSjmatthew 	/* wait for soft reset to clear */
1336aa14606aSjmatthew 	for (i = 0; i < 1000; i++) {
1337aa14606aSjmatthew 		if ((qla_read(sc, QLA_CTRL_STATUS) & QLA_CTRL_RESET) == 0)
1338aa14606aSjmatthew 			break;
1339aa14606aSjmatthew 
1340aa14606aSjmatthew 		delay(100);
1341aa14606aSjmatthew 	}
1342aa14606aSjmatthew 
1343aa14606aSjmatthew 	if (i == 1000) {
1344f901d880Sjmatthew 		DPRINTF(QLA_D_INTR, "%s: reset didn't clear\n", DEVNAME(sc));
1345aa14606aSjmatthew 		qla_set_ints(sc, 0);
1346aa14606aSjmatthew 		return (ENXIO);
1347aa14606aSjmatthew 	}
1348aa14606aSjmatthew 
1349aa14606aSjmatthew 	/* reset FPM */
1350aa14606aSjmatthew 	qla_write(sc, QLA_CTRL_STATUS, QLA_CTRL_FPM0_REGS);
1351aa14606aSjmatthew 	qla_write(sc, QLA_FPM_DIAG, QLA_FPM_RESET);
1352aa14606aSjmatthew 	qla_write(sc, QLA_FPM_DIAG, 0);	/* isp(4) doesn't do this? */
1353aa14606aSjmatthew 	qla_write(sc, QLA_CTRL_STATUS, QLA_CTRL_RISC_REGS);
1354aa14606aSjmatthew 
1355aa14606aSjmatthew 	/* reset risc processor */
1356aa14606aSjmatthew 	qla_host_cmd(sc, QLA_HOST_CMD_RESET);
1357aa14606aSjmatthew 	delay(100);
1358aa14606aSjmatthew 	qla_write(sc, QLA_SEMA, 0);
1359aa14606aSjmatthew 	qla_host_cmd(sc, QLA_HOST_CMD_MASK_PARITY);	/* from isp(4) */
1360aa14606aSjmatthew 	qla_host_cmd(sc, QLA_HOST_CMD_RELEASE);
1361aa14606aSjmatthew 
1362aa14606aSjmatthew 	/* reset queue pointers */
136368858f63Sdlg 	qla_queue_write(sc, sc->sc_regs->req_in, 0);
136468858f63Sdlg 	qla_queue_write(sc, sc->sc_regs->req_out, 0);
136568858f63Sdlg 	qla_queue_write(sc, sc->sc_regs->res_in, 0);
136668858f63Sdlg 	qla_queue_write(sc, sc->sc_regs->res_out, 0);
1367aa14606aSjmatthew 
1368aa14606aSjmatthew 	qla_set_ints(sc, 1);
1369aa14606aSjmatthew 	/* isp(4) sends QLA_HOST_CMD_BIOS here.. not documented? */
1370aa14606aSjmatthew 
1371aa14606aSjmatthew 	/* do a basic mailbox operation to check we're alive */
1372aa14606aSjmatthew 	sc->sc_mbox[0] = QLA_MBOX_NOP;
137386376500Sjmatthew 	if (qla_mbox(sc, 0x0001)) {
1374f901d880Sjmatthew 		DPRINTF(QLA_D_INTR, "%s: ISP not responding after reset\n",
1375f901d880Sjmatthew 		    DEVNAME(sc));
1376aa14606aSjmatthew 		return (ENXIO);
1377aa14606aSjmatthew 	}
1378aa14606aSjmatthew 
1379aa14606aSjmatthew 	return (0);
1380aa14606aSjmatthew }
1381aa14606aSjmatthew 
1382aa14606aSjmatthew void
qla_update_topology(struct qla_softc * sc)1383aa14606aSjmatthew qla_update_topology(struct qla_softc *sc)
1384aa14606aSjmatthew {
1385aa14606aSjmatthew 	sc->sc_mbox[0] = QLA_MBOX_GET_LOOP_ID;
138686376500Sjmatthew 	if (qla_mbox(sc, 0x0001)) {
1387f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: unable to get loop id\n", DEVNAME(sc));
1388aa14606aSjmatthew 		sc->sc_topology = QLA_TOPO_N_PORT_NO_TARGET;
1389aa14606aSjmatthew 	} else {
1390aa14606aSjmatthew 		sc->sc_topology = sc->sc_mbox[6];
1391aa14606aSjmatthew 		sc->sc_loop_id = sc->sc_mbox[1];
1392aa14606aSjmatthew 
1393aa14606aSjmatthew 		switch (sc->sc_topology) {
1394aa14606aSjmatthew 		case QLA_TOPO_NL_PORT:
1395aa14606aSjmatthew 		case QLA_TOPO_N_PORT:
1396f901d880Sjmatthew 			DPRINTF(QLA_D_PORT, "%s: loop id %d\n", DEVNAME(sc),
1397aa14606aSjmatthew 			    sc->sc_loop_id);
1398aa14606aSjmatthew 			break;
1399aa14606aSjmatthew 
1400aa14606aSjmatthew 		case QLA_TOPO_FL_PORT:
1401aa14606aSjmatthew 		case QLA_TOPO_F_PORT:
1402aa14606aSjmatthew 			sc->sc_port_id = sc->sc_mbox[2] |
1403aa14606aSjmatthew 			    (sc->sc_mbox[3] << 16);
1404f901d880Sjmatthew 			DPRINTF(QLA_D_PORT, "%s: fabric port id %06x\n",
1405f901d880Sjmatthew 			    DEVNAME(sc), sc->sc_port_id);
1406aa14606aSjmatthew 			break;
1407aa14606aSjmatthew 
1408aa14606aSjmatthew 		case QLA_TOPO_N_PORT_NO_TARGET:
1409aa14606aSjmatthew 		default:
1410f901d880Sjmatthew 			DPRINTF(QLA_D_PORT, "%s: not connected\n", DEVNAME(sc));
1411aa14606aSjmatthew 			break;
1412aa14606aSjmatthew 		}
1413aa14606aSjmatthew 
1414aa14606aSjmatthew 		switch (sc->sc_topology) {
1415aa14606aSjmatthew 		case QLA_TOPO_NL_PORT:
1416aa14606aSjmatthew 		case QLA_TOPO_FL_PORT:
1417aa14606aSjmatthew 			sc->sc_loop_max_id = 126;
1418aa14606aSjmatthew 			break;
1419aa14606aSjmatthew 
1420aa14606aSjmatthew 		case QLA_TOPO_N_PORT:
1421aa14606aSjmatthew 			sc->sc_loop_max_id = 2;
1422aa14606aSjmatthew 			break;
1423aa14606aSjmatthew 
1424aa14606aSjmatthew 		default:
1425aa14606aSjmatthew 			sc->sc_loop_max_id = 0;
1426aa14606aSjmatthew 			break;
1427aa14606aSjmatthew 		}
1428aa14606aSjmatthew 	}
1429aa14606aSjmatthew }
1430aa14606aSjmatthew 
1431aa14606aSjmatthew int
qla_update_fabric(struct qla_softc * sc)1432aa14606aSjmatthew qla_update_fabric(struct qla_softc *sc)
1433aa14606aSjmatthew {
1434aa14606aSjmatthew 	struct qla_sns_rft_id *rft;
1435aa14606aSjmatthew 
1436aa14606aSjmatthew 	if (sc->sc_fabric == 0)
1437944e332aSjmatthew 		return (0);
1438aa14606aSjmatthew 
1439aa14606aSjmatthew 	switch (sc->sc_topology) {
1440aa14606aSjmatthew 	case QLA_TOPO_F_PORT:
1441aa14606aSjmatthew 	case QLA_TOPO_FL_PORT:
1442aa14606aSjmatthew 		break;
1443aa14606aSjmatthew 
1444aa14606aSjmatthew 	default:
1445944e332aSjmatthew 		return (0);
1446aa14606aSjmatthew 	}
1447aa14606aSjmatthew 
1448aa14606aSjmatthew 	/* get the name server's port db entry */
1449aa14606aSjmatthew 	sc->sc_mbox[0] = QLA_MBOX_GET_PORT_DB;
1450aa14606aSjmatthew 	if (sc->sc_2k_logins) {
1451aa14606aSjmatthew 		sc->sc_mbox[1] = QLA_F_PORT_HANDLE;
1452aa14606aSjmatthew 	} else {
1453aa14606aSjmatthew 		sc->sc_mbox[1] = QLA_F_PORT_HANDLE << 8;
1454aa14606aSjmatthew 	}
1455aa14606aSjmatthew 	qla_mbox_putaddr(sc->sc_mbox, sc->sc_scratch);
1456aa14606aSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
1457aa14606aSjmatthew 	    sizeof(struct qla_get_port_db), BUS_DMASYNC_PREREAD);
145886376500Sjmatthew 	if (qla_mbox(sc, 0x00cf)) {
1459f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: get port db for SNS failed: %x\n",
1460aa14606aSjmatthew 		    DEVNAME(sc), sc->sc_mbox[0]);
1461aa14606aSjmatthew 		sc->sc_sns_port_name = 0;
1462aa14606aSjmatthew 	} else {
1463aa14606aSjmatthew 		struct qla_get_port_db *pdb;
1464aa14606aSjmatthew 		bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
1465aa14606aSjmatthew 		    sizeof(struct qla_get_port_db), BUS_DMASYNC_POSTREAD);
1466aa14606aSjmatthew 		pdb = QLA_DMA_KVA(sc->sc_scratch);
1467f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: SNS port name %llx\n", DEVNAME(sc),
1468aa14606aSjmatthew 		    betoh64(pdb->port_name));
1469aa14606aSjmatthew 		sc->sc_sns_port_name = betoh64(pdb->port_name);
1470aa14606aSjmatthew 	}
1471aa14606aSjmatthew 
1472aa14606aSjmatthew 	/*
1473aa14606aSjmatthew 	 * register fc4 types with the fabric
1474aa14606aSjmatthew 	 * some switches do this automatically, but apparently
1475aa14606aSjmatthew 	 * some don't.
1476aa14606aSjmatthew 	 */
1477aa14606aSjmatthew 	rft = QLA_DMA_KVA(sc->sc_scratch);
1478aa14606aSjmatthew 	memset(rft, 0, sizeof(*rft) + sizeof(struct qla_sns_req_hdr));
1479aa14606aSjmatthew 	rft->subcmd = htole16(QLA_SNS_RFT_ID);
1480aa14606aSjmatthew 	rft->max_word = htole16(sizeof(struct qla_sns_req_hdr) / 4);
1481aa14606aSjmatthew 	rft->port_id = htole32(sc->sc_port_id);
148294a1a047Sjmatthew 	rft->fc4_types[0] = htole32(1 << QLA_FC4_SCSI);
1483aa14606aSjmatthew 	if (qla_sns_req(sc, sc->sc_scratch, sizeof(*rft))) {
1484f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: RFT_ID failed\n", DEVNAME(sc));
1485aa14606aSjmatthew 		/* we might be able to continue after this fails */
1486aa14606aSjmatthew 	}
1487aa14606aSjmatthew 
1488944e332aSjmatthew 	return (1);
1489aa14606aSjmatthew }
1490aa14606aSjmatthew 
1491d10165e5Sjmatthew int
qla_get_port_name_list(struct qla_softc * sc,u_int32_t match)1492d10165e5Sjmatthew qla_get_port_name_list(struct qla_softc *sc, u_int32_t match)
1493d10165e5Sjmatthew {
1494d10165e5Sjmatthew 	int i;
1495d10165e5Sjmatthew 	struct qla_port_name_list *l;
1496d10165e5Sjmatthew 	struct qla_fc_port *port;
1497d10165e5Sjmatthew 
1498d10165e5Sjmatthew 	sc->sc_mbox[0] = QLA_MBOX_GET_PORT_NAME_LIST;
1499d10165e5Sjmatthew 	sc->sc_mbox[1] = 0x08;	/* include initiators */
1500d10165e5Sjmatthew 	if (match & QLA_LOCATION_FABRIC)
1501d10165e5Sjmatthew 		sc->sc_mbox[1] |= 0x02;	 /* return all loop ids */
1502d10165e5Sjmatthew 	qla_mbox_putaddr(sc->sc_mbox, sc->sc_scratch);
1503d10165e5Sjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
1504d10165e5Sjmatthew 	    QLA_DMA_LEN(sc->sc_scratch), BUS_DMASYNC_PREREAD);
150586376500Sjmatthew 	if (qla_mbox(sc, 0x04f)) {
1506d10165e5Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: get port name list failed: %x\n",
1507d10165e5Sjmatthew 		    DEVNAME(sc), sc->sc_mbox[0]);
1508d10165e5Sjmatthew 		return (1);
1509d10165e5Sjmatthew 	}
1510d10165e5Sjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
1511d10165e5Sjmatthew 	    QLA_DMA_LEN(sc->sc_scratch), BUS_DMASYNC_PREREAD);
1512d10165e5Sjmatthew 
1513d10165e5Sjmatthew 	i = 0;
1514d10165e5Sjmatthew 	l = QLA_DMA_KVA(sc->sc_scratch);
1515cb06bc72Sjmatthew 	mtx_enter(&sc->sc_port_mtx);
1516d10165e5Sjmatthew 	while (i * sizeof(*l) < sc->sc_mbox[1]) {
1517d10165e5Sjmatthew 		u_int16_t loopid;
1518d10165e5Sjmatthew 		u_int32_t loc;
1519d10165e5Sjmatthew 
1520d10165e5Sjmatthew 		loopid = letoh16(l[i].loop_id);
1521cb06bc72Sjmatthew 		/* skip special ports */
1522cb06bc72Sjmatthew 		switch (loopid) {
1523cb06bc72Sjmatthew 		case QLA_F_PORT_HANDLE:
1524cb06bc72Sjmatthew 		case QLA_SNS_HANDLE:
1525cb06bc72Sjmatthew 		case QLA_FABRIC_CTRL_HANDLE:
1526cb06bc72Sjmatthew 			loc = 0;
1527cb06bc72Sjmatthew 			break;
1528cb06bc72Sjmatthew 		default:
1529cb06bc72Sjmatthew 			if (loopid <= sc->sc_loop_max_id) {
1530cb06bc72Sjmatthew 				loc = QLA_LOCATION_LOOP_ID(loopid);
1531cb06bc72Sjmatthew 			} else {
1532cb06bc72Sjmatthew 				/*
1533cb06bc72Sjmatthew 				 * we don't have the port id here, so just
1534cb06bc72Sjmatthew 				 * indicate it's a fabric port.
1535cb06bc72Sjmatthew 				 */
1536cb06bc72Sjmatthew 				loc = QLA_LOCATION_FABRIC;
1537cb06bc72Sjmatthew 			}
1538cb06bc72Sjmatthew 		}
1539d10165e5Sjmatthew 
1540d10165e5Sjmatthew 		if (match & loc) {
1541d10165e5Sjmatthew 			port = malloc(sizeof(*port), M_DEVBUF, M_ZERO |
1542d10165e5Sjmatthew 			    M_NOWAIT);
1543cb06bc72Sjmatthew 			if (port == NULL) {
1544cb06bc72Sjmatthew 				printf("%s: failed to allocate port struct\n",
1545cb06bc72Sjmatthew 				    DEVNAME(sc));
1546cb06bc72Sjmatthew 				break;
1547cb06bc72Sjmatthew 			}
1548cb06bc72Sjmatthew 			port->location = loc;
1549d10165e5Sjmatthew 			port->loopid = loopid;
1550d10165e5Sjmatthew 			port->port_name = letoh64(l[i].port_name);
1551d10165e5Sjmatthew 			DPRINTF(QLA_D_PORT, "%s: loop id %d, port name %llx\n",
1552d10165e5Sjmatthew 			    DEVNAME(sc), port->loopid, port->port_name);
1553d10165e5Sjmatthew 			TAILQ_INSERT_TAIL(&sc->sc_ports_found, port, update);
1554d10165e5Sjmatthew 		}
1555d10165e5Sjmatthew 		i++;
1556d10165e5Sjmatthew 	}
1557cb06bc72Sjmatthew 	mtx_leave(&sc->sc_port_mtx);
1558d10165e5Sjmatthew 
1559d10165e5Sjmatthew 	return (0);
1560d10165e5Sjmatthew }
1561d10165e5Sjmatthew 
1562aa14606aSjmatthew struct qla_fc_port *
qla_next_fabric_port(struct qla_softc * sc,u_int32_t * firstport,u_int32_t * lastport)1563aa14606aSjmatthew qla_next_fabric_port(struct qla_softc *sc, u_int32_t *firstport,
1564aa14606aSjmatthew     u_int32_t *lastport)
1565aa14606aSjmatthew {
1566aa14606aSjmatthew 	struct qla_sns_ga_nxt *ga;
1567aa14606aSjmatthew 	struct qla_sns_ga_nxt_resp *gar;
1568aa14606aSjmatthew 	struct qla_fc_port *fport;
1569aa14606aSjmatthew 	int result;
1570aa14606aSjmatthew 
1571aa14606aSjmatthew 	/* get the next port from the fabric nameserver */
1572aa14606aSjmatthew 	ga = QLA_DMA_KVA(sc->sc_scratch);
1573aa14606aSjmatthew 	memset(ga, 0, sizeof(*ga) + sizeof(*gar));
1574aa14606aSjmatthew 	ga->subcmd = htole16(QLA_SNS_GA_NXT);
1575aa14606aSjmatthew 	ga->max_word = htole16(sizeof(*gar) / 4);
1576aa14606aSjmatthew 	ga->port_id = htole32(*lastport);
1577aa14606aSjmatthew 	result = qla_sns_req(sc, sc->sc_scratch, sizeof(*ga));
1578aa14606aSjmatthew 	if (result) {
1579d0f795e9Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: GA_NXT %06x failed: %x\n", DEVNAME(sc),
1580d0f795e9Sjmatthew 		    *lastport, result);
1581aa14606aSjmatthew 		*lastport = 0xffffffff;
1582aa14606aSjmatthew 		return (NULL);
1583aa14606aSjmatthew 	}
1584aa14606aSjmatthew 
1585aa14606aSjmatthew 	gar = (struct qla_sns_ga_nxt_resp *)(ga + 1);
1586aa14606aSjmatthew 	/* if the response is all zeroes, try again */
1587aa14606aSjmatthew 	if (gar->port_type_id == 0 && gar->port_name == 0 &&
1588aa14606aSjmatthew 	    gar->node_name == 0) {
1589f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: GA_NXT returned junk\n", DEVNAME(sc));
1590aa14606aSjmatthew 		return (NULL);
1591aa14606aSjmatthew 	}
1592aa14606aSjmatthew 
1593aa14606aSjmatthew 	/* are we back at the start? */
1594aa14606aSjmatthew 	*lastport = betoh32(gar->port_type_id) & 0xffffff;
1595aa14606aSjmatthew 	if (*lastport == *firstport) {
1596aa14606aSjmatthew 		*lastport = 0xffffffff;
1597aa14606aSjmatthew 		return (NULL);
1598aa14606aSjmatthew 	}
1599aa14606aSjmatthew 	if (*firstport == 0xffffffff)
1600aa14606aSjmatthew 		*firstport = *lastport;
1601aa14606aSjmatthew 
1602d0f795e9Sjmatthew 	DPRINTF(QLA_D_PORT, "%s: GA_NXT: port id: %06x, wwpn %llx, wwnn %llx\n",
1603aa14606aSjmatthew 	    DEVNAME(sc), *lastport, betoh64(gar->port_name),
160494a1a047Sjmatthew 	    betoh64(gar->node_name));
1605aa14606aSjmatthew 
1606aa14606aSjmatthew 	/* don't try to log in to ourselves */
1607aa14606aSjmatthew 	if (*lastport == sc->sc_port_id) {
1608aa14606aSjmatthew 		return (NULL);
1609aa14606aSjmatthew 	}
1610aa14606aSjmatthew 
1611aa14606aSjmatthew 	fport = malloc(sizeof(*fport), M_DEVBUF, M_ZERO | M_NOWAIT);
1612aa14606aSjmatthew 	if (fport == NULL) {
1613aa14606aSjmatthew 		printf("%s: failed to allocate a port struct\n",
1614aa14606aSjmatthew 		    DEVNAME(sc));
1615aa14606aSjmatthew 		*lastport = 0xffffffff;
1616aa14606aSjmatthew 		return (NULL);
1617aa14606aSjmatthew 	}
1618aa14606aSjmatthew 	fport->port_name = betoh64(gar->port_name);
1619aa14606aSjmatthew 	fport->node_name = betoh64(gar->node_name);
1620aa14606aSjmatthew 	fport->location = QLA_LOCATION_PORT_ID(*lastport);
1621aa14606aSjmatthew 	fport->portid = *lastport;
1622aa14606aSjmatthew 	return (fport);
1623aa14606aSjmatthew }
1624aa14606aSjmatthew 
1625aa14606aSjmatthew int
qla_fabric_plogi(struct qla_softc * sc,struct qla_fc_port * port)1626aa14606aSjmatthew qla_fabric_plogi(struct qla_softc *sc, struct qla_fc_port *port)
1627aa14606aSjmatthew {
1628944e332aSjmatthew 	int loopid, mboxin, err;
1629944e332aSjmatthew 	u_int32_t id;
1630aa14606aSjmatthew 
163174521a28Sjmatthew 	loopid = 0;
1632944e332aSjmatthew retry:
163374521a28Sjmatthew 	if (port->loopid == 0) {
1634aa14606aSjmatthew 		mtx_enter(&sc->sc_port_mtx);
163574521a28Sjmatthew 		loopid = qla_get_loop_id(sc, loopid);
1636aa14606aSjmatthew 		mtx_leave(&sc->sc_port_mtx);
1637aa14606aSjmatthew 		if (loopid == -1) {
1638f901d880Sjmatthew 			DPRINTF(QLA_D_PORT, "%s: ran out of loop ids\n",
1639aa14606aSjmatthew 			    DEVNAME(sc));
1640aa14606aSjmatthew 			return (1);
1641aa14606aSjmatthew 		}
1642944e332aSjmatthew 	}
1643aa14606aSjmatthew 
1644aa14606aSjmatthew 	mboxin = 0x000f;
1645aa14606aSjmatthew 	sc->sc_mbox[0] = QLA_MBOX_FABRIC_PLOGI;
1646aa14606aSjmatthew 	sc->sc_mbox[2] = (port->portid >> 16) & 0xff;
1647aa14606aSjmatthew 	sc->sc_mbox[3] = port->portid & 0xffff;
1648aa14606aSjmatthew 	if (sc->sc_2k_logins) {
1649aa14606aSjmatthew 		sc->sc_mbox[1] = loopid;
1650aa14606aSjmatthew 		sc->sc_mbox[10] = 0;
1651aa14606aSjmatthew 		mboxin |= (1 << 10);
1652aa14606aSjmatthew 	} else {
1653aa14606aSjmatthew 		sc->sc_mbox[1] = loopid << 8;
1654aa14606aSjmatthew 	}
1655aa14606aSjmatthew 
1656944e332aSjmatthew 	err = qla_mbox(sc, mboxin);
1657944e332aSjmatthew 	switch (err) {
1658944e332aSjmatthew 	case 0:
1659944e332aSjmatthew 		DPRINTF(QLA_D_PORT, "%s: logged in to %06x as %d\n",
1660944e332aSjmatthew 		    DEVNAME(sc), port->portid, loopid);
1661944e332aSjmatthew 		port->flags &= ~QLA_PORT_FLAG_NEEDS_LOGIN;
1662aa14606aSjmatthew 		port->loopid = loopid;
1663aa14606aSjmatthew 		return (0);
1664944e332aSjmatthew 
1665944e332aSjmatthew 	case QLA_MBOX_PORT_USED:
1666944e332aSjmatthew 		DPRINTF(QLA_D_PORT, "%s: already logged in to %06x as %d\n",
1667944e332aSjmatthew 		    DEVNAME(sc), port->portid, sc->sc_mbox[1]);
1668944e332aSjmatthew 		port->flags &= ~QLA_PORT_FLAG_NEEDS_LOGIN;
1669944e332aSjmatthew 		port->loopid = sc->sc_mbox[1];
1670944e332aSjmatthew 		return (0);
1671944e332aSjmatthew 
1672944e332aSjmatthew 	case QLA_MBOX_LOOP_USED:
1673944e332aSjmatthew 		id = (sc->sc_mbox[1] << 16) | sc->sc_mbox[2];
167474521a28Sjmatthew 		if (qla_add_logged_in_port(sc, loopid, id)) {
1675944e332aSjmatthew 			return (1);
1676944e332aSjmatthew 		}
1677944e332aSjmatthew 		port->loopid = 0;
167874521a28Sjmatthew 		loopid++;
167974521a28Sjmatthew 		goto retry;
1680944e332aSjmatthew 
1681944e332aSjmatthew 	default:
1682944e332aSjmatthew 		DPRINTF(QLA_D_PORT, "%s: error %x logging in to port %06x\n",
1683944e332aSjmatthew 		    DEVNAME(sc), err, port->portid);
1684944e332aSjmatthew 		port->loopid = 0;
1685944e332aSjmatthew 		return (1);
1686944e332aSjmatthew 	}
1687aa14606aSjmatthew }
1688aa14606aSjmatthew 
1689aa14606aSjmatthew void
qla_fabric_plogo(struct qla_softc * sc,struct qla_fc_port * port)1690aa14606aSjmatthew qla_fabric_plogo(struct qla_softc *sc, struct qla_fc_port *port)
1691aa14606aSjmatthew {
1692aa14606aSjmatthew 	int mboxin = 0x0003;
1693aa14606aSjmatthew 	sc->sc_mbox[0] = QLA_MBOX_FABRIC_PLOGO;
1694aa14606aSjmatthew 	if (sc->sc_2k_logins) {
1695aa14606aSjmatthew 		sc->sc_mbox[1] = port->loopid;
1696aa14606aSjmatthew 		sc->sc_mbox[10] = 0;
1697aa14606aSjmatthew 		mboxin |= (1 << 10);
1698aa14606aSjmatthew 	} else {
1699aa14606aSjmatthew 		sc->sc_mbox[1] = port->loopid << 8;
1700aa14606aSjmatthew 	}
1701aa14606aSjmatthew 
170286376500Sjmatthew 	if (qla_mbox(sc, mboxin))
1703d0f795e9Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: loop id %d logout failed\n",
1704f901d880Sjmatthew 		    DEVNAME(sc), port->loopid);
1705aa14606aSjmatthew }
1706aa14606aSjmatthew 
17074cd89cf2Sjmatthew void
qla_update_done(struct qla_softc * sc,int task)1708944e332aSjmatthew qla_update_done(struct qla_softc *sc, int task)
17094cd89cf2Sjmatthew {
1710944e332aSjmatthew 	atomic_clearbits_int(&sc->sc_update_tasks, task);
1711944e332aSjmatthew }
1712944e332aSjmatthew 
1713944e332aSjmatthew void
qla_update_start(struct qla_softc * sc,int task)1714944e332aSjmatthew qla_update_start(struct qla_softc *sc, int task)
1715944e332aSjmatthew {
1716944e332aSjmatthew 	atomic_setbits_int(&sc->sc_update_tasks, task);
1717944e332aSjmatthew 	task_add(sc->sc_update_taskq, &sc->sc_update_task);
1718944e332aSjmatthew }
1719944e332aSjmatthew 
1720944e332aSjmatthew void
qla_clear_port_lists(struct qla_softc * sc)1721944e332aSjmatthew qla_clear_port_lists(struct qla_softc *sc)
1722944e332aSjmatthew {
1723944e332aSjmatthew 	struct qla_fc_port *p;
1724289dc1f7Sderaadt 
1725944e332aSjmatthew 	while (!TAILQ_EMPTY(&sc->sc_ports_found)) {
1726944e332aSjmatthew 		p = TAILQ_FIRST(&sc->sc_ports_found);
1727944e332aSjmatthew 		TAILQ_REMOVE(&sc->sc_ports_found, p, update);
1728289dc1f7Sderaadt 		free(p, M_DEVBUF, sizeof *p);
1729944e332aSjmatthew 	}
1730944e332aSjmatthew 
1731944e332aSjmatthew 	while (!TAILQ_EMPTY(&sc->sc_ports_new)) {
1732944e332aSjmatthew 		p = TAILQ_FIRST(&sc->sc_ports_new);
1733944e332aSjmatthew 		TAILQ_REMOVE(&sc->sc_ports_new, p, update);
1734289dc1f7Sderaadt 		free(p, M_DEVBUF, sizeof *p);
1735944e332aSjmatthew 	}
1736944e332aSjmatthew 
1737944e332aSjmatthew 	while (!TAILQ_EMPTY(&sc->sc_ports_gone)) {
1738944e332aSjmatthew 		p = TAILQ_FIRST(&sc->sc_ports_gone);
1739944e332aSjmatthew 		TAILQ_REMOVE(&sc->sc_ports_gone, p, update);
1740944e332aSjmatthew 	}
1741944e332aSjmatthew }
1742944e332aSjmatthew 
1743944e332aSjmatthew void
qla_do_update(void * xsc)1744e4195480Sdlg qla_do_update(void *xsc)
1745944e332aSjmatthew {
1746944e332aSjmatthew 	struct qla_softc *sc = xsc;
1747944e332aSjmatthew 	int firstport, lastport;
1748944e332aSjmatthew 	struct qla_fc_port *port, *fport;
1749944e332aSjmatthew 
1750944e332aSjmatthew 	DPRINTF(QLA_D_PORT, "%s: updating\n", DEVNAME(sc));
1751944e332aSjmatthew 	while (sc->sc_update_tasks != 0) {
1752944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_CLEAR_ALL) {
1753944e332aSjmatthew 			TAILQ_HEAD(, qla_fc_port) detach;
1754944e332aSjmatthew 			DPRINTF(QLA_D_PORT, "%s: detaching everything\n",
1755944e332aSjmatthew 			    DEVNAME(sc));
1756944e332aSjmatthew 
1757944e332aSjmatthew 			mtx_enter(&sc->sc_port_mtx);
1758944e332aSjmatthew 			qla_clear_port_lists(sc);
1759944e332aSjmatthew 			TAILQ_INIT(&detach);
1760e86c2bbeSbket 			TAILQ_CONCAT(&detach, &sc->sc_ports, ports);
1761944e332aSjmatthew 			mtx_leave(&sc->sc_port_mtx);
1762944e332aSjmatthew 
1763944e332aSjmatthew 			while (!TAILQ_EMPTY(&detach)) {
1764944e332aSjmatthew 				port = TAILQ_FIRST(&detach);
1765944e332aSjmatthew 				TAILQ_REMOVE(&detach, port, ports);
1766944e332aSjmatthew 				if (port->flags & QLA_PORT_FLAG_IS_TARGET) {
1767944e332aSjmatthew 					scsi_detach_target(sc->sc_scsibus,
1768cc5e21deSkrw 					    port->loopid, DETACH_FORCE |
1769cc5e21deSkrw 					    DETACH_QUIET);
1770944e332aSjmatthew 				}
1771944e332aSjmatthew 				sc->sc_targets[port->loopid] = NULL;
1772944e332aSjmatthew 				if (port->location & QLA_LOCATION_FABRIC)
1773944e332aSjmatthew 					qla_fabric_plogo(sc, port);
1774944e332aSjmatthew 
1775289dc1f7Sderaadt 				free(port, M_DEVBUF, sizeof *port);
1776944e332aSjmatthew 			}
1777944e332aSjmatthew 
1778944e332aSjmatthew 			qla_update_done(sc, QLA_UPDATE_TASK_CLEAR_ALL);
1779944e332aSjmatthew 			continue;
1780944e332aSjmatthew 		}
1781944e332aSjmatthew 
1782944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_SOFTRESET) {
1783944e332aSjmatthew 			/* what no */
1784944e332aSjmatthew 			qla_update_done(sc, QLA_UPDATE_TASK_SOFTRESET);
1785944e332aSjmatthew 			continue;
1786944e332aSjmatthew 		}
1787944e332aSjmatthew 
1788944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_UPDATE_TOPO) {
1789944e332aSjmatthew 			DPRINTF(QLA_D_PORT, "%s: updating topology\n",
1790944e332aSjmatthew 			    DEVNAME(sc));
1791944e332aSjmatthew 			qla_update_topology(sc);
1792944e332aSjmatthew 			qla_update_done(sc, QLA_UPDATE_TASK_UPDATE_TOPO);
1793944e332aSjmatthew 			continue;
1794944e332aSjmatthew 		}
1795944e332aSjmatthew 
1796944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_GET_PORT_LIST) {
1797944e332aSjmatthew 			DPRINTF(QLA_D_PORT, "%s: getting port name list\n",
1798944e332aSjmatthew 			    DEVNAME(sc));
1799944e332aSjmatthew 			mtx_enter(&sc->sc_port_mtx);
1800944e332aSjmatthew 			qla_clear_port_lists(sc);
1801944e332aSjmatthew 			mtx_leave(&sc->sc_port_mtx);
1802944e332aSjmatthew 
1803944e332aSjmatthew 			qla_get_port_name_list(sc, QLA_LOCATION_LOOP |
1804944e332aSjmatthew 			    QLA_LOCATION_FABRIC);
1805944e332aSjmatthew 			mtx_enter(&sc->sc_port_mtx);
1806944e332aSjmatthew 			TAILQ_FOREACH(port, &sc->sc_ports, ports) {
1807944e332aSjmatthew 				TAILQ_INSERT_TAIL(&sc->sc_ports_gone, port,
1808944e332aSjmatthew 				    update);
1809944e332aSjmatthew 				if (port->location & QLA_LOCATION_FABRIC) {
1810944e332aSjmatthew 					port->flags |=
1811944e332aSjmatthew 					    QLA_PORT_FLAG_NEEDS_LOGIN;
1812944e332aSjmatthew 				}
1813944e332aSjmatthew 			}
1814944e332aSjmatthew 
1815944e332aSjmatthew 			/* take care of ports that haven't changed first */
1816944e332aSjmatthew 			TAILQ_FOREACH(fport, &sc->sc_ports_found, update) {
1817944e332aSjmatthew 				port = sc->sc_targets[fport->loopid];
1818944e332aSjmatthew 				if (port == NULL || fport->port_name !=
1819944e332aSjmatthew 				    port->port_name) {
1820944e332aSjmatthew 					/* new or changed port, handled later */
1821944e332aSjmatthew 					continue;
1822944e332aSjmatthew 				}
1823944e332aSjmatthew 
1824944e332aSjmatthew 				/*
1825944e332aSjmatthew 				 * the port hasn't been logged out, which
1826944e332aSjmatthew 				 * means we don't need to log in again, and,
1827944e332aSjmatthew 				 * for loop ports, that the port still exists.
1828944e332aSjmatthew 				 */
1829944e332aSjmatthew 				port->flags &= ~QLA_PORT_FLAG_NEEDS_LOGIN;
1830944e332aSjmatthew 				if (port->location & QLA_LOCATION_LOOP)
1831944e332aSjmatthew 					TAILQ_REMOVE(&sc->sc_ports_gone,
1832944e332aSjmatthew 					    port, update);
1833944e332aSjmatthew 
1834944e332aSjmatthew 				fport->location = 0;
1835944e332aSjmatthew 			}
1836944e332aSjmatthew 			mtx_leave(&sc->sc_port_mtx);
1837944e332aSjmatthew 			qla_update_start(sc, QLA_UPDATE_TASK_PORT_LIST);
1838944e332aSjmatthew 			qla_update_done(sc, QLA_UPDATE_TASK_GET_PORT_LIST);
1839944e332aSjmatthew 			continue;
1840944e332aSjmatthew 		}
1841944e332aSjmatthew 
1842944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_PORT_LIST) {
1843944e332aSjmatthew 			mtx_enter(&sc->sc_port_mtx);
1844944e332aSjmatthew 			fport = TAILQ_FIRST(&sc->sc_ports_found);
1845944e332aSjmatthew 			if (fport != NULL) {
1846944e332aSjmatthew 				TAILQ_REMOVE(&sc->sc_ports_found, fport,
1847944e332aSjmatthew 				    update);
1848944e332aSjmatthew 			}
1849944e332aSjmatthew 			mtx_leave(&sc->sc_port_mtx);
1850944e332aSjmatthew 
1851944e332aSjmatthew 			if (fport == NULL) {
1852944e332aSjmatthew 				DPRINTF(QLA_D_PORT, "%s: done with ports\n",
1853944e332aSjmatthew 				    DEVNAME(sc));
1854944e332aSjmatthew 				qla_update_done(sc,
1855944e332aSjmatthew 				    QLA_UPDATE_TASK_PORT_LIST);
1856944e332aSjmatthew 				qla_update_start(sc,
1857944e332aSjmatthew 				    QLA_UPDATE_TASK_SCAN_FABRIC);
1858944e332aSjmatthew 			} else if (fport->location & QLA_LOCATION_LOOP) {
1859944e332aSjmatthew 				DPRINTF(QLA_D_PORT, "%s: loop port %d\n",
1860944e332aSjmatthew 				    DEVNAME(sc), fport->loopid);
1861944e332aSjmatthew 				if (qla_add_loop_port(sc, fport) != 0)
1862289dc1f7Sderaadt 					free(fport, M_DEVBUF, sizeof *fport);
1863944e332aSjmatthew 			} else if (fport->location & QLA_LOCATION_FABRIC) {
1864944e332aSjmatthew 				qla_add_fabric_port(sc, fport);
1865944e332aSjmatthew 			} else {
1866944e332aSjmatthew 				/* already processed */
1867289dc1f7Sderaadt 				free(fport, M_DEVBUF, sizeof *fport);
1868944e332aSjmatthew 			}
1869944e332aSjmatthew 			continue;
1870944e332aSjmatthew 		}
1871944e332aSjmatthew 
1872944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_SCAN_FABRIC) {
1873944e332aSjmatthew 			DPRINTF(QLA_D_PORT, "%s: starting fabric scan\n",
1874944e332aSjmatthew 			    DEVNAME(sc));
1875944e332aSjmatthew 			lastport = sc->sc_port_id;
1876944e332aSjmatthew 			firstport = 0xffffffff;
1877944e332aSjmatthew 			if (qla_update_fabric(sc))
1878944e332aSjmatthew 				qla_update_start(sc,
1879944e332aSjmatthew 				    QLA_UPDATE_TASK_SCANNING_FABRIC);
1880944e332aSjmatthew 			qla_update_done(sc, QLA_UPDATE_TASK_SCAN_FABRIC);
1881944e332aSjmatthew 			continue;
1882944e332aSjmatthew 		}
1883944e332aSjmatthew 
1884944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_SCANNING_FABRIC) {
1885944e332aSjmatthew 			fport = qla_next_fabric_port(sc, &firstport, &lastport);
1886944e332aSjmatthew 			if (fport != NULL) {
1887944e332aSjmatthew 				int disp;
1888944e332aSjmatthew 
1889944e332aSjmatthew 				mtx_enter(&sc->sc_port_mtx);
1890944e332aSjmatthew 				disp = qla_classify_port(sc, fport->location,
1891944e332aSjmatthew 				    fport->port_name, fport->node_name, &port);
1892944e332aSjmatthew 				switch (disp) {
1893944e332aSjmatthew 				case QLA_PORT_DISP_CHANGED:
1894944e332aSjmatthew 				case QLA_PORT_DISP_MOVED:
1895944e332aSjmatthew 					/* we'll log out the old port later */
1896944e332aSjmatthew 				case QLA_PORT_DISP_NEW:
1897944e332aSjmatthew 					DPRINTF(QLA_D_PORT, "%s: new port "
1898944e332aSjmatthew 					    "%06x\n", DEVNAME(sc),
1899944e332aSjmatthew 					    fport->portid);
1900944e332aSjmatthew 					TAILQ_INSERT_TAIL(&sc->sc_ports_found,
1901944e332aSjmatthew 					    fport, update);
1902944e332aSjmatthew 					break;
1903944e332aSjmatthew 				case QLA_PORT_DISP_DUP:
1904289dc1f7Sderaadt 					free(fport, M_DEVBUF, sizeof *fport);
1905944e332aSjmatthew 					break;
1906944e332aSjmatthew 				case QLA_PORT_DISP_SAME:
1907944e332aSjmatthew 					DPRINTF(QLA_D_PORT, "%s: existing port"
1908944e332aSjmatthew 					    " %06x\n", DEVNAME(sc),
1909944e332aSjmatthew 					    fport->portid);
1910944e332aSjmatthew 					TAILQ_REMOVE(&sc->sc_ports_gone, port,
1911944e332aSjmatthew 					    update);
1912289dc1f7Sderaadt 					free(fport, M_DEVBUF, sizeof *fport);
1913944e332aSjmatthew 					break;
1914944e332aSjmatthew 				}
1915944e332aSjmatthew 				mtx_leave(&sc->sc_port_mtx);
1916944e332aSjmatthew 			}
1917944e332aSjmatthew 			if (lastport == 0xffffffff) {
1918944e332aSjmatthew 				DPRINTF(QLA_D_PORT, "%s: finished\n",
1919944e332aSjmatthew 				    DEVNAME(sc));
1920944e332aSjmatthew 				qla_update_done(sc,
1921944e332aSjmatthew 				    QLA_UPDATE_TASK_SCANNING_FABRIC);
1922944e332aSjmatthew 				qla_update_start(sc,
1923944e332aSjmatthew 				    QLA_UPDATE_TASK_FABRIC_LOGIN);
1924944e332aSjmatthew 			}
1925944e332aSjmatthew 			continue;
1926944e332aSjmatthew 		}
1927944e332aSjmatthew 
1928944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_FABRIC_LOGIN) {
1929944e332aSjmatthew 			mtx_enter(&sc->sc_port_mtx);
1930944e332aSjmatthew 			port = TAILQ_FIRST(&sc->sc_ports_found);
1931944e332aSjmatthew 			if (port != NULL) {
1932944e332aSjmatthew 				TAILQ_REMOVE(&sc->sc_ports_found, port, update);
1933944e332aSjmatthew 			}
1934944e332aSjmatthew 			mtx_leave(&sc->sc_port_mtx);
1935944e332aSjmatthew 
1936944e332aSjmatthew 			if (port != NULL) {
1937944e332aSjmatthew 				DPRINTF(QLA_D_PORT, "%s: found port %06x\n",
1938944e332aSjmatthew 				    DEVNAME(sc), port->portid);
1939944e332aSjmatthew 				if (qla_fabric_plogi(sc, port) == 0) {
1940944e332aSjmatthew 					qla_add_fabric_port(sc, port);
1941944e332aSjmatthew 				} else {
1942289dc1f7Sderaadt 					free(port, M_DEVBUF, sizeof *port);
1943944e332aSjmatthew 				}
1944944e332aSjmatthew 			} else {
1945944e332aSjmatthew 				DPRINTF(QLA_D_PORT, "%s: done with logins\n",
1946944e332aSjmatthew 				    DEVNAME(sc));
1947944e332aSjmatthew 				qla_update_done(sc,
1948944e332aSjmatthew 				    QLA_UPDATE_TASK_FABRIC_LOGIN);
1949944e332aSjmatthew 				qla_update_start(sc,
1950944e332aSjmatthew 				    QLA_UPDATE_TASK_ATTACH_TARGET |
1951944e332aSjmatthew 				    QLA_UPDATE_TASK_DETACH_TARGET);
1952944e332aSjmatthew 			}
1953944e332aSjmatthew 			continue;
1954944e332aSjmatthew 		}
1955944e332aSjmatthew 
1956944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_FABRIC_RELOGIN) {
1957944e332aSjmatthew 			TAILQ_FOREACH(port, &sc->sc_ports, ports) {
1958944e332aSjmatthew 				if (port->flags & QLA_PORT_FLAG_NEEDS_LOGIN) {
1959944e332aSjmatthew 					qla_fabric_plogi(sc, port);
1960944e332aSjmatthew 					break;
1961944e332aSjmatthew 				}
1962944e332aSjmatthew 			}
1963944e332aSjmatthew 
1964abcbcc4dSdoug 			if (port == NULL)
1965944e332aSjmatthew 				qla_update_done(sc,
1966944e332aSjmatthew 				    QLA_UPDATE_TASK_FABRIC_RELOGIN);
1967944e332aSjmatthew 			continue;
1968944e332aSjmatthew 		}
1969944e332aSjmatthew 
1970944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_DETACH_TARGET) {
1971944e332aSjmatthew 			mtx_enter(&sc->sc_port_mtx);
1972944e332aSjmatthew 			port = TAILQ_FIRST(&sc->sc_ports_gone);
1973944e332aSjmatthew 			if (port != NULL) {
1974944e332aSjmatthew 				sc->sc_targets[port->loopid] = NULL;
1975944e332aSjmatthew 				TAILQ_REMOVE(&sc->sc_ports_gone, port, update);
1976944e332aSjmatthew 				TAILQ_REMOVE(&sc->sc_ports, port, ports);
1977944e332aSjmatthew 			}
1978944e332aSjmatthew 			mtx_leave(&sc->sc_port_mtx);
1979944e332aSjmatthew 
1980944e332aSjmatthew 			if (port != NULL) {
1981944e332aSjmatthew 				DPRINTF(QLA_D_PORT, "%s: detaching target %d\n",
1982944e332aSjmatthew 				    DEVNAME(sc), port->loopid);
1983944e332aSjmatthew 				if (sc->sc_scsibus != NULL)
1984944e332aSjmatthew 					scsi_detach_target(sc->sc_scsibus,
1985cc5e21deSkrw 					    port->loopid, DETACH_FORCE |
1986cc5e21deSkrw 					    DETACH_QUIET);
1987944e332aSjmatthew 
1988944e332aSjmatthew 				if (port->location & QLA_LOCATION_FABRIC)
1989944e332aSjmatthew 					qla_fabric_plogo(sc, port);
1990944e332aSjmatthew 
1991289dc1f7Sderaadt 				free(port, M_DEVBUF, sizeof *port);
1992944e332aSjmatthew 			} else {
1993944e332aSjmatthew 				qla_update_done(sc,
1994944e332aSjmatthew 				    QLA_UPDATE_TASK_DETACH_TARGET);
1995944e332aSjmatthew 			}
1996944e332aSjmatthew 			continue;
1997944e332aSjmatthew 		}
1998944e332aSjmatthew 
1999944e332aSjmatthew 		if (sc->sc_update_tasks & QLA_UPDATE_TASK_ATTACH_TARGET) {
2000944e332aSjmatthew 			mtx_enter(&sc->sc_port_mtx);
2001944e332aSjmatthew 			port = TAILQ_FIRST(&sc->sc_ports_new);
2002944e332aSjmatthew 			if (port != NULL) {
2003944e332aSjmatthew 				TAILQ_REMOVE(&sc->sc_ports_new, port, update);
2004944e332aSjmatthew 				TAILQ_INSERT_TAIL(&sc->sc_ports, port, ports);
2005944e332aSjmatthew 			}
2006944e332aSjmatthew 			mtx_leave(&sc->sc_port_mtx);
2007944e332aSjmatthew 
2008944e332aSjmatthew 			if (port != NULL) {
2009944e332aSjmatthew 				if (sc->sc_scsibus != NULL)
2010944e332aSjmatthew 					scsi_probe_target(sc->sc_scsibus,
2011944e332aSjmatthew 					    port->loopid);
2012944e332aSjmatthew 			} else {
2013944e332aSjmatthew 				qla_update_done(sc,
2014944e332aSjmatthew 				    QLA_UPDATE_TASK_ATTACH_TARGET);
2015944e332aSjmatthew 			}
2016944e332aSjmatthew 			continue;
2017944e332aSjmatthew 		}
2018944e332aSjmatthew 
2019944e332aSjmatthew 	}
2020944e332aSjmatthew 
2021944e332aSjmatthew 	DPRINTF(QLA_D_PORT, "%s: done updating\n", DEVNAME(sc));
20224cd89cf2Sjmatthew }
20234cd89cf2Sjmatthew 
20244cd89cf2Sjmatthew int
qla_async(struct qla_softc * sc,u_int16_t info)20254cd89cf2Sjmatthew qla_async(struct qla_softc *sc, u_int16_t info)
20264cd89cf2Sjmatthew {
20274cd89cf2Sjmatthew 	u_int16_t id, exp;
20284cd89cf2Sjmatthew 
20294cd89cf2Sjmatthew 	switch (info) {
20304cd89cf2Sjmatthew 	case QLA_ASYNC_SYSTEM_ERROR:
2031944e332aSjmatthew 		qla_update_start(sc, QLA_UPDATE_TASK_SOFTRESET);
20324cd89cf2Sjmatthew 		break;
20334cd89cf2Sjmatthew 
20344cd89cf2Sjmatthew 	case QLA_ASYNC_REQ_XFER_ERROR:
2035944e332aSjmatthew 		qla_update_start(sc, QLA_UPDATE_TASK_SOFTRESET);
20364cd89cf2Sjmatthew 		break;
20374cd89cf2Sjmatthew 
20384cd89cf2Sjmatthew 	case QLA_ASYNC_RSP_XFER_ERROR:
2039944e332aSjmatthew 		qla_update_start(sc, QLA_UPDATE_TASK_SOFTRESET);
20404cd89cf2Sjmatthew 		break;
20414cd89cf2Sjmatthew 
20424cd89cf2Sjmatthew 	case QLA_ASYNC_LIP_OCCURRED:
2043f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: lip occurred\n", DEVNAME(sc));
20444cd89cf2Sjmatthew 		break;
20454cd89cf2Sjmatthew 
20464cd89cf2Sjmatthew 	case QLA_ASYNC_LOOP_UP:
2047f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: loop up\n", DEVNAME(sc));
20484cd89cf2Sjmatthew 		sc->sc_loop_up = 1;
20494cd89cf2Sjmatthew 		sc->sc_marker_required = 1;
2050944e332aSjmatthew 		qla_update_start(sc, QLA_UPDATE_TASK_UPDATE_TOPO |
2051944e332aSjmatthew 		    QLA_UPDATE_TASK_GET_PORT_LIST);
20524cd89cf2Sjmatthew 		break;
20534cd89cf2Sjmatthew 
20544cd89cf2Sjmatthew 	case QLA_ASYNC_LOOP_DOWN:
2055f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: loop down\n", DEVNAME(sc));
20564cd89cf2Sjmatthew 		sc->sc_loop_up = 0;
2057944e332aSjmatthew 		qla_update_start(sc, QLA_UPDATE_TASK_CLEAR_ALL);
20584cd89cf2Sjmatthew 		break;
20594cd89cf2Sjmatthew 
20604cd89cf2Sjmatthew 	case QLA_ASYNC_LIP_RESET:
2061f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: lip reset\n", DEVNAME(sc));
20624cd89cf2Sjmatthew 		sc->sc_marker_required = 1;
2063944e332aSjmatthew 		qla_update_start(sc, QLA_UPDATE_TASK_FABRIC_RELOGIN);
20644cd89cf2Sjmatthew 		break;
20654cd89cf2Sjmatthew 
20664cd89cf2Sjmatthew 	case QLA_ASYNC_PORT_DB_CHANGE:
2067f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: port db changed %x\n", DEVNAME(sc),
20684cd89cf2Sjmatthew 		    qla_read_mbox(sc, 1));
2069944e332aSjmatthew 		qla_update_start(sc, QLA_UPDATE_TASK_GET_PORT_LIST);
20704cd89cf2Sjmatthew 		break;
20714cd89cf2Sjmatthew 
20724cd89cf2Sjmatthew 	case QLA_ASYNC_CHANGE_NOTIFY:
2073f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: name server change (%02x:%02x)\n",
2074f901d880Sjmatthew 		    DEVNAME(sc), qla_read_mbox(sc, 1), qla_read_mbox(sc, 2));
2075944e332aSjmatthew 		qla_update_start(sc, QLA_UPDATE_TASK_GET_PORT_LIST);
20764cd89cf2Sjmatthew 		break;
20774cd89cf2Sjmatthew 
20784cd89cf2Sjmatthew 	case QLA_ASYNC_LIP_F8:
2079f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: lip f8\n", DEVNAME(sc));
20804cd89cf2Sjmatthew 		break;
20814cd89cf2Sjmatthew 
20824cd89cf2Sjmatthew 	case QLA_ASYNC_LOOP_INIT_ERROR:
2083f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: loop initialization error: %x\n",
2084f901d880Sjmatthew 		    DEVNAME(sc), qla_read_mbox(sc, 1));
20854cd89cf2Sjmatthew 		break;
20864cd89cf2Sjmatthew 
20874cd89cf2Sjmatthew 	case QLA_ASYNC_LOGIN_REJECT:
20884cd89cf2Sjmatthew 		id = qla_read_mbox(sc, 1);
20894cd89cf2Sjmatthew 		exp = qla_read_mbox(sc, 2);
2090f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: login reject from %x (reason %d,"
2091f901d880Sjmatthew 		    " explanation %x)\n", DEVNAME(sc), id >> 8, id & 0xff, exp);
20924cd89cf2Sjmatthew 		break;
20934cd89cf2Sjmatthew 
20944cd89cf2Sjmatthew 	case QLA_ASYNC_SCSI_CMD_COMPLETE:
20954cd89cf2Sjmatthew 		/* shouldn't happen, we disable fast posting */
20964cd89cf2Sjmatthew 		break;
20974cd89cf2Sjmatthew 
20984cd89cf2Sjmatthew 	case QLA_ASYNC_CTIO_COMPLETE:
20994cd89cf2Sjmatthew 		/* definitely shouldn't happen, we don't do target mode */
21004cd89cf2Sjmatthew 		break;
21014cd89cf2Sjmatthew 
21024cd89cf2Sjmatthew 	case QLA_ASYNC_POINT_TO_POINT:
2103f901d880Sjmatthew 		DPRINTF(QLA_D_PORT, "%s: connected in point-to-point mode\n",
2104f901d880Sjmatthew 		    DEVNAME(sc));
21054cd89cf2Sjmatthew 		/* we get stuck handling these if we have the wrong loop
21064cd89cf2Sjmatthew 		 * topology; should somehow reinit with different things
21074cd89cf2Sjmatthew 		 * somehow.
21084cd89cf2Sjmatthew 		 */
21094cd89cf2Sjmatthew 		break;
21104cd89cf2Sjmatthew 
21114cd89cf2Sjmatthew 	case QLA_ASYNC_ZIO_RESP_UPDATE:
21124cd89cf2Sjmatthew 		/* shouldn't happen, we don't do zio */
21134cd89cf2Sjmatthew 		break;
21144cd89cf2Sjmatthew 
21154cd89cf2Sjmatthew 	case QLA_ASYNC_RND_ERROR:
21164cd89cf2Sjmatthew 		/* do nothing? */
21174cd89cf2Sjmatthew 		break;
21184cd89cf2Sjmatthew 
21194cd89cf2Sjmatthew 	case QLA_ASYNC_QUEUE_FULL:
21204cd89cf2Sjmatthew 		break;
21214cd89cf2Sjmatthew 
21224cd89cf2Sjmatthew 	default:
2123f901d880Sjmatthew 		DPRINTF(QLA_D_INTR, "%s: unknown async %x\n", DEVNAME(sc),
2124f901d880Sjmatthew 		    info);
21254cd89cf2Sjmatthew 		break;
21264cd89cf2Sjmatthew 	}
21274cd89cf2Sjmatthew 	return (1);
21284cd89cf2Sjmatthew }
21294cd89cf2Sjmatthew 
21304cd89cf2Sjmatthew void
qla_dump_iocb(struct qla_softc * sc,void * buf)21314cd89cf2Sjmatthew qla_dump_iocb(struct qla_softc *sc, void *buf)
21324cd89cf2Sjmatthew {
2133f901d880Sjmatthew #ifdef QLA_DEBUG
21344cd89cf2Sjmatthew 	u_int8_t *iocb = buf;
21354cd89cf2Sjmatthew 	int l;
21364cd89cf2Sjmatthew 	int b;
21374cd89cf2Sjmatthew 
2138f901d880Sjmatthew 	if ((qladebug & QLA_D_IOCB) == 0)
2139f901d880Sjmatthew 		return;
2140f901d880Sjmatthew 
21414cd89cf2Sjmatthew 	printf("%s: iocb:\n", DEVNAME(sc));
21424cd89cf2Sjmatthew 	for (l = 0; l < 4; l++) {
21434cd89cf2Sjmatthew 		for (b = 0; b < 16; b++) {
21444cd89cf2Sjmatthew 			printf(" %2.2x", iocb[(l*16)+b]);
21454cd89cf2Sjmatthew 		}
21464cd89cf2Sjmatthew 		printf("\n");
21474cd89cf2Sjmatthew 	}
2148f901d880Sjmatthew #endif
21494cd89cf2Sjmatthew }
21504cd89cf2Sjmatthew 
21514cd89cf2Sjmatthew void
qla_dump_iocb_segs(struct qla_softc * sc,void * segs,int n)21524cd89cf2Sjmatthew qla_dump_iocb_segs(struct qla_softc *sc, void *segs, int n)
21534cd89cf2Sjmatthew {
2154f901d880Sjmatthew #ifdef QLA_DEBUG
21554cd89cf2Sjmatthew 	u_int8_t *buf = segs;
21564cd89cf2Sjmatthew 	int s, b;
2157f901d880Sjmatthew 	if ((qladebug & QLA_D_IOCB) == 0)
2158f901d880Sjmatthew 		return;
2159f901d880Sjmatthew 
21604cd89cf2Sjmatthew 	printf("%s: iocb segs:\n", DEVNAME(sc));
21614cd89cf2Sjmatthew 	for (s = 0; s < n; s++) {
21624cd89cf2Sjmatthew 		for (b = 0; b < sizeof(struct qla_iocb_seg); b++) {
21634cd89cf2Sjmatthew 			printf(" %2.2x", buf[(s*(sizeof(struct qla_iocb_seg)))
21644cd89cf2Sjmatthew 			    + b]);
21654cd89cf2Sjmatthew 		}
21664cd89cf2Sjmatthew 		printf("\n");
21674cd89cf2Sjmatthew 	}
2168f901d880Sjmatthew #endif
21694cd89cf2Sjmatthew }
21704cd89cf2Sjmatthew 
21714cd89cf2Sjmatthew void
qla_put_marker(struct qla_softc * sc,void * buf)21724cd89cf2Sjmatthew qla_put_marker(struct qla_softc *sc, void *buf)
21734cd89cf2Sjmatthew {
21744cd89cf2Sjmatthew 	struct qla_iocb_marker *marker = buf;
21754cd89cf2Sjmatthew 
21764cd89cf2Sjmatthew 	marker->entry_type = QLA_IOCB_MARKER;
21774cd89cf2Sjmatthew 	marker->entry_count = 1;
21784cd89cf2Sjmatthew 	marker->seqno = 0;
21794cd89cf2Sjmatthew 	marker->flags = 0;
21804cd89cf2Sjmatthew 
21814cd89cf2Sjmatthew 	/* could be more specific here; isp(4) isn't */
21824cd89cf2Sjmatthew 	marker->target = 0;
21834cd89cf2Sjmatthew 	marker->modifier = QLA_IOCB_MARKER_SYNC_ALL;
2184f901d880Sjmatthew 	qla_dump_iocb(sc, buf);
21854cd89cf2Sjmatthew }
21864cd89cf2Sjmatthew 
218791637d79Sdlg static inline void
qla_put_data_seg(struct qla_iocb_seg * seg,bus_dmamap_t dmap,int num)21884cd89cf2Sjmatthew qla_put_data_seg(struct qla_iocb_seg *seg, bus_dmamap_t dmap, int num)
21894cd89cf2Sjmatthew {
2190ee8c2243Sdlg 	uint64_t addr = dmap->dm_segs[num].ds_addr;
2191ee8c2243Sdlg 
2192ee8c2243Sdlg 	htolem32(&seg->seg_addr_lo, addr);
2193ee8c2243Sdlg 	htolem32(&seg->seg_addr_hi, addr >> 32);
2194ee8c2243Sdlg 	htolem32(&seg->seg_len, dmap->dm_segs[num].ds_len);
21954cd89cf2Sjmatthew }
21964cd89cf2Sjmatthew 
21974cd89cf2Sjmatthew void
qla_put_cmd(struct qla_softc * sc,void * buf,struct scsi_xfer * xs,struct qla_ccb * ccb)21984cd89cf2Sjmatthew qla_put_cmd(struct qla_softc *sc, void *buf, struct scsi_xfer *xs,
21994cd89cf2Sjmatthew     struct qla_ccb *ccb)
22004cd89cf2Sjmatthew {
22014cd89cf2Sjmatthew 	struct qla_iocb_req34 *req = buf;
22024cd89cf2Sjmatthew 	u_int16_t dir;
22034cd89cf2Sjmatthew 	int seg;
22044cd89cf2Sjmatthew 	int target = xs->sc_link->target;
22054cd89cf2Sjmatthew 
22064cd89cf2Sjmatthew 	req->seqno = 0;
22074cd89cf2Sjmatthew 	req->flags = 0;
22084cd89cf2Sjmatthew 	req->entry_count = 1;
22094cd89cf2Sjmatthew 
22104cd89cf2Sjmatthew 	if (xs->datalen == 0) {
22114cd89cf2Sjmatthew 		dir = QLA_IOCB_CMD_NO_DATA;
22124cd89cf2Sjmatthew 		req->req_seg_count = 0;
22134cd89cf2Sjmatthew 		req->entry_type = QLA_IOCB_CMD_TYPE_3;
22144cd89cf2Sjmatthew 	} else {
22154cd89cf2Sjmatthew 		dir = xs->flags & SCSI_DATA_IN ? QLA_IOCB_CMD_READ_DATA :
22164cd89cf2Sjmatthew 		    QLA_IOCB_CMD_WRITE_DATA;
221791637d79Sdlg 		htolem16(&req->req_seg_count, ccb->ccb_dmamap->dm_nsegs);
22184cd89cf2Sjmatthew 		if (ccb->ccb_dmamap->dm_nsegs > QLA_IOCB_SEGS_PER_CMD) {
22194cd89cf2Sjmatthew 			req->entry_type = QLA_IOCB_CMD_TYPE_4;
22204cd89cf2Sjmatthew 			for (seg = 0; seg < ccb->ccb_dmamap->dm_nsegs; seg++) {
22214cd89cf2Sjmatthew 				qla_put_data_seg(&ccb->ccb_t4segs[seg],
22224cd89cf2Sjmatthew 				    ccb->ccb_dmamap, seg);
22234cd89cf2Sjmatthew 			}
22244cd89cf2Sjmatthew 			req->req_type.req4.req4_seg_type = htole16(1);
22254cd89cf2Sjmatthew 			req->req_type.req4.req4_seg_base = 0;
222691637d79Sdlg 			req->req_type.req4.req4_seg_addr = ccb->ccb_seg_dva;
22274cd89cf2Sjmatthew 			memset(req->req_type.req4.req4_reserved, 0,
22284cd89cf2Sjmatthew 			    sizeof(req->req_type.req4.req4_reserved));
22294cd89cf2Sjmatthew 			bus_dmamap_sync(sc->sc_dmat,
22304cd89cf2Sjmatthew 			    QLA_DMA_MAP(sc->sc_segments), ccb->ccb_seg_offset,
22314cd89cf2Sjmatthew 			    sizeof(*ccb->ccb_t4segs) * ccb->ccb_dmamap->dm_nsegs,
22324cd89cf2Sjmatthew 			    BUS_DMASYNC_PREWRITE);
22334cd89cf2Sjmatthew 		} else {
22344cd89cf2Sjmatthew 			req->entry_type = QLA_IOCB_CMD_TYPE_3;
22354cd89cf2Sjmatthew 			for (seg = 0; seg < ccb->ccb_dmamap->dm_nsegs; seg++) {
22364cd89cf2Sjmatthew 				qla_put_data_seg(&req->req_type.req3_segs[seg],
22374cd89cf2Sjmatthew 				    ccb->ccb_dmamap, seg);
22384cd89cf2Sjmatthew 			}
22394cd89cf2Sjmatthew 		}
22404cd89cf2Sjmatthew 	}
22414cd89cf2Sjmatthew 
22424cd89cf2Sjmatthew 	/* isp(4) uses head of queue for 'request sense' commands */
224391637d79Sdlg 	htolem16(&req->req_flags, QLA_IOCB_CMD_SIMPLE_QUEUE | dir);
22444cd89cf2Sjmatthew 
22454cd89cf2Sjmatthew 	/*
22464cd89cf2Sjmatthew 	 * timeout is in seconds.  make sure it's at least 1 if a timeout
22474cd89cf2Sjmatthew 	 * was specified in xs
22484cd89cf2Sjmatthew 	 */
22494cd89cf2Sjmatthew 	if (xs->timeout != 0)
225091637d79Sdlg 		htolem16(&req->req_time, MAX(1, xs->timeout/1000));
22514cd89cf2Sjmatthew 
22524cd89cf2Sjmatthew 	/* lun and target layout vary with firmware attributes */
22534cd89cf2Sjmatthew 	if (sc->sc_expanded_lun) {
22544cd89cf2Sjmatthew 		if (sc->sc_2k_logins) {
22554cd89cf2Sjmatthew 			req->req_target = htole16(target);
22564cd89cf2Sjmatthew 		} else {
22574cd89cf2Sjmatthew 			req->req_target = htole16(target << 8);
22584cd89cf2Sjmatthew 		}
22594cd89cf2Sjmatthew 		req->req_scclun = htole16(xs->sc_link->lun);
22604cd89cf2Sjmatthew 	} else {
22614cd89cf2Sjmatthew 		req->req_target = htole16(target << 8 | xs->sc_link->lun);
22624cd89cf2Sjmatthew 	}
2263664c6166Skrw 	memcpy(req->req_cdb, &xs->cmd, xs->cmdlen);
22644cd89cf2Sjmatthew 	req->req_totalcnt = htole32(xs->datalen);
22654cd89cf2Sjmatthew 
22664cd89cf2Sjmatthew 	req->req_handle = ccb->ccb_id;
2267f901d880Sjmatthew 
2268f901d880Sjmatthew 	qla_dump_iocb(sc, buf);
22694cd89cf2Sjmatthew }
22704cd89cf2Sjmatthew 
22710732819bSjmatthew int
qla_verify_firmware(struct qla_softc * sc,u_int16_t addr)22720732819bSjmatthew qla_verify_firmware(struct qla_softc *sc, u_int16_t addr)
22730732819bSjmatthew {
22740732819bSjmatthew 	sc->sc_mbox[0] = QLA_MBOX_VERIFY_CSUM;
22750732819bSjmatthew 	sc->sc_mbox[1] = addr;
227686376500Sjmatthew 	return (qla_mbox(sc, 0x0003));
22770732819bSjmatthew }
22780732819bSjmatthew 
227995622914Sderaadt #ifndef ISP_NOFIRMWARE
22804cd89cf2Sjmatthew int
qla_load_firmware_words(struct qla_softc * sc,const u_int16_t * src,u_int16_t dest)22819eb00056Sjmatthew qla_load_firmware_words(struct qla_softc *sc, const u_int16_t *src,
22829eb00056Sjmatthew     u_int16_t dest)
2283db1d60ccSjmatthew {
2284db1d60ccSjmatthew 	u_int16_t i;
2285db1d60ccSjmatthew 
2286db1d60ccSjmatthew 	for (i = 0; i < src[3]; i++) {
2287db1d60ccSjmatthew 		sc->sc_mbox[0] = QLA_MBOX_WRITE_RAM_WORD;
22889eb00056Sjmatthew 		sc->sc_mbox[1] = i + dest;
2289db1d60ccSjmatthew 		sc->sc_mbox[2] = src[i];
229086376500Sjmatthew 		if (qla_mbox(sc, 0x07)) {
2291db1d60ccSjmatthew 			printf("firmware load failed\n");
2292db1d60ccSjmatthew 			return (1);
2293db1d60ccSjmatthew 		}
2294db1d60ccSjmatthew 	}
2295db1d60ccSjmatthew 
22960732819bSjmatthew 	return (qla_verify_firmware(sc, dest));
2297db1d60ccSjmatthew }
2298db1d60ccSjmatthew 
2299db1d60ccSjmatthew int
qla_load_firmware_2100(struct qla_softc * sc)23009eb00056Sjmatthew qla_load_firmware_2100(struct qla_softc *sc)
23019eb00056Sjmatthew {
23029eb00056Sjmatthew 	return qla_load_firmware_words(sc, isp_2100_risc_code,
23039eb00056Sjmatthew 	    QLA_2100_CODE_ORG);
23049eb00056Sjmatthew }
23059eb00056Sjmatthew 
23069eb00056Sjmatthew int
qla_load_firmware_2200(struct qla_softc * sc)23079eb00056Sjmatthew qla_load_firmware_2200(struct qla_softc *sc)
23089eb00056Sjmatthew {
23099eb00056Sjmatthew 	return qla_load_firmware_words(sc, isp_2200_risc_code,
23109eb00056Sjmatthew 	    QLA_2200_CODE_ORG);
23119eb00056Sjmatthew }
23129eb00056Sjmatthew 
23139eb00056Sjmatthew int
qla_load_fwchunk_2300(struct qla_softc * sc,struct qla_dmamem * mem,const u_int16_t * src,u_int32_t dest)2314db1d60ccSjmatthew qla_load_fwchunk_2300(struct qla_softc *sc, struct qla_dmamem *mem,
2315db1d60ccSjmatthew     const u_int16_t *src, u_int32_t dest)
23164cd89cf2Sjmatthew {
23174cd89cf2Sjmatthew 	u_int16_t origin, done, total;
23184cd89cf2Sjmatthew 	int i;
23194cd89cf2Sjmatthew 
23204cd89cf2Sjmatthew 	origin = dest;
23214cd89cf2Sjmatthew 	done = 0;
23224cd89cf2Sjmatthew 	total = src[3];
23234cd89cf2Sjmatthew 
23244cd89cf2Sjmatthew 	while (done < total) {
23254cd89cf2Sjmatthew 		u_int16_t *copy;
23264cd89cf2Sjmatthew 		u_int32_t words;
23274cd89cf2Sjmatthew 
23284cd89cf2Sjmatthew 		/* limit transfer size otherwise it just doesn't work */
23294cd89cf2Sjmatthew 		words = MIN(total - done, 1 << 10);
23304cd89cf2Sjmatthew 		copy = QLA_DMA_KVA(mem);
23314cd89cf2Sjmatthew 		for (i = 0; i < words; i++) {
23324cd89cf2Sjmatthew 			copy[i] = htole16(src[done++]);
23334cd89cf2Sjmatthew 		}
23344cd89cf2Sjmatthew 		bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0, words * 2,
23354cd89cf2Sjmatthew 		    BUS_DMASYNC_PREWRITE);
23364cd89cf2Sjmatthew 
23374cd89cf2Sjmatthew 		sc->sc_mbox[0] = QLA_MBOX_LOAD_RAM_EXT;
23384cd89cf2Sjmatthew 		sc->sc_mbox[1] = dest;
23394cd89cf2Sjmatthew 		sc->sc_mbox[4] = words;
23404cd89cf2Sjmatthew 		sc->sc_mbox[8] = dest >> 16;
23414cd89cf2Sjmatthew 		qla_mbox_putaddr(sc->sc_mbox, mem);
234286376500Sjmatthew 		if (qla_mbox(sc, 0x01ff)) {
23434cd89cf2Sjmatthew 			printf("firmware load failed\n");
23444cd89cf2Sjmatthew 			return (1);
23454cd89cf2Sjmatthew 		}
23464cd89cf2Sjmatthew 		bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0, words * 2,
23474cd89cf2Sjmatthew 		    BUS_DMASYNC_POSTWRITE);
23484cd89cf2Sjmatthew 
23494cd89cf2Sjmatthew 		dest += words;
23504cd89cf2Sjmatthew 	}
23514cd89cf2Sjmatthew 
23520732819bSjmatthew 	return (qla_verify_firmware(sc, origin));
23534cd89cf2Sjmatthew }
23544cd89cf2Sjmatthew 
23554cd89cf2Sjmatthew int
qla_load_firmware_2300(struct qla_softc * sc)23564cd89cf2Sjmatthew qla_load_firmware_2300(struct qla_softc *sc)
23574cd89cf2Sjmatthew {
23584cd89cf2Sjmatthew 	struct qla_dmamem *mem;
23594cd89cf2Sjmatthew 	const u_int16_t *fw = isp_2300_risc_code;
236033f4f584Sjmatthew 	int rv;
236133f4f584Sjmatthew 
236233f4f584Sjmatthew 	mem = qla_dmamem_alloc(sc, 65536);
236333f4f584Sjmatthew 	rv = qla_load_fwchunk_2300(sc, mem, fw, QLA_2300_CODE_ORG);
236433f4f584Sjmatthew 	qla_dmamem_free(sc, mem);
236533f4f584Sjmatthew 
236633f4f584Sjmatthew 	return (rv);
236733f4f584Sjmatthew }
236833f4f584Sjmatthew 
236933f4f584Sjmatthew int
qla_load_firmware_2322(struct qla_softc * sc)237033f4f584Sjmatthew qla_load_firmware_2322(struct qla_softc *sc)
237133f4f584Sjmatthew {
237233f4f584Sjmatthew 	/* we don't have the 2322 firmware image yet */
237333f4f584Sjmatthew #if 0
237433f4f584Sjmatthew 	struct qla_dmamem *mem;
237533f4f584Sjmatthew 	const u_int16_t *fw = isp_2322_risc_code;
237633f4f584Sjmatthew 	u_int32_t addr;
237733f4f584Sjmatthew 	int i;
23784cd89cf2Sjmatthew 
23794cd89cf2Sjmatthew 	mem = qla_dmamem_alloc(sc, 65536);
23800732819bSjmatthew 	if (qla_load_fwchunk_2300(sc, mem, fw, QLA_2300_CODE_ORG)) {
23810732819bSjmatthew 		qla_dmamem_free(sc, mem);
23820732819bSjmatthew 		return (1);
23830732819bSjmatthew 	}
23844cd89cf2Sjmatthew 
23854cd89cf2Sjmatthew 	for (i = 0; i < 2; i++) {
23864cd89cf2Sjmatthew 		fw += fw[3];
23874cd89cf2Sjmatthew 		addr = fw[5] | ((fw[4] & 0x3f) << 16);
23880732819bSjmatthew 		if (qla_load_fwchunk_2300(sc, mem, fw, addr)) {
23890732819bSjmatthew 			qla_dmamem_free(sc, mem);
23900732819bSjmatthew 			return (1);
23910732819bSjmatthew 		}
23924cd89cf2Sjmatthew 	}
23934cd89cf2Sjmatthew 
23944cd89cf2Sjmatthew 	qla_dmamem_free(sc, mem);
239533f4f584Sjmatthew #endif
23964cd89cf2Sjmatthew 	return (0);
23974cd89cf2Sjmatthew }
23984cd89cf2Sjmatthew 
239995622914Sderaadt #endif	/* !ISP_NOFIRMWARE */
2400fa72eff0Sjmatthew 
24014cd89cf2Sjmatthew int
qla_read_nvram(struct qla_softc * sc)24024cd89cf2Sjmatthew qla_read_nvram(struct qla_softc *sc)
24034cd89cf2Sjmatthew {
24044cd89cf2Sjmatthew 	u_int16_t data[sizeof(sc->sc_nvram) >> 1];
24054cd89cf2Sjmatthew 	u_int16_t req, cmd, val;
24064cd89cf2Sjmatthew 	u_int8_t csum;
24074cd89cf2Sjmatthew 	int i, base, bit;
24084cd89cf2Sjmatthew 
24094cd89cf2Sjmatthew 	base = sc->sc_port * 0x80;
24104cd89cf2Sjmatthew 
24114cd89cf2Sjmatthew 	qla_write(sc, QLA_NVRAM, QLA_NVRAM_CHIP_SEL);
24124cd89cf2Sjmatthew 	delay(10);
24134cd89cf2Sjmatthew 	qla_write(sc, QLA_NVRAM, QLA_NVRAM_CHIP_SEL | QLA_NVRAM_CLOCK);
24144cd89cf2Sjmatthew 	delay(10);
24154cd89cf2Sjmatthew 
24164cd89cf2Sjmatthew 	for (i = 0; i < nitems(data); i++) {
24174cd89cf2Sjmatthew 		req = (i + base) | (QLA_NVRAM_CMD_READ << 8);
24184cd89cf2Sjmatthew 
24194cd89cf2Sjmatthew 		/* write each bit out through the nvram register */
24204cd89cf2Sjmatthew 		for (bit = 10; bit >= 0; bit--) {
24214cd89cf2Sjmatthew 			cmd = QLA_NVRAM_CHIP_SEL;
24224cd89cf2Sjmatthew 			if ((req >> bit) & 1) {
24234cd89cf2Sjmatthew 				cmd |= QLA_NVRAM_DATA_OUT;
24244cd89cf2Sjmatthew 			}
24254cd89cf2Sjmatthew 			qla_write(sc, QLA_NVRAM, cmd);
24264cd89cf2Sjmatthew 			delay(10);
24274cd89cf2Sjmatthew 			qla_read(sc, QLA_NVRAM);
24284cd89cf2Sjmatthew 
24294cd89cf2Sjmatthew 			qla_write(sc, QLA_NVRAM, cmd | QLA_NVRAM_CLOCK);
24304cd89cf2Sjmatthew 			delay(10);
24314cd89cf2Sjmatthew 			qla_read(sc, QLA_NVRAM);
24324cd89cf2Sjmatthew 
24334cd89cf2Sjmatthew 			qla_write(sc, QLA_NVRAM, cmd);
24344cd89cf2Sjmatthew 			delay(10);
24354cd89cf2Sjmatthew 			qla_read(sc, QLA_NVRAM);
24364cd89cf2Sjmatthew 		}
24374cd89cf2Sjmatthew 
24384cd89cf2Sjmatthew 		/* read the result back */
24394cd89cf2Sjmatthew 		val = 0;
24404cd89cf2Sjmatthew 		for (bit = 0; bit < 16; bit++) {
24414cd89cf2Sjmatthew 			val <<= 1;
24424cd89cf2Sjmatthew 			qla_write(sc, QLA_NVRAM, QLA_NVRAM_CHIP_SEL |
24434cd89cf2Sjmatthew 			    QLA_NVRAM_CLOCK);
24444cd89cf2Sjmatthew 			delay(10);
24454cd89cf2Sjmatthew 			if (qla_read(sc, QLA_NVRAM) & QLA_NVRAM_DATA_IN)
24464cd89cf2Sjmatthew 				val |= 1;
24474cd89cf2Sjmatthew 			delay(10);
24484cd89cf2Sjmatthew 
24494cd89cf2Sjmatthew 			qla_write(sc, QLA_NVRAM, QLA_NVRAM_CHIP_SEL);
24504cd89cf2Sjmatthew 			delay(10);
24514cd89cf2Sjmatthew 			qla_read(sc, QLA_NVRAM);
24524cd89cf2Sjmatthew 		}
24534cd89cf2Sjmatthew 
24544cd89cf2Sjmatthew 		qla_write(sc, QLA_NVRAM, 0);
24554cd89cf2Sjmatthew 		delay(10);
24564cd89cf2Sjmatthew 		qla_read(sc, QLA_NVRAM);
24574cd89cf2Sjmatthew 
24584cd89cf2Sjmatthew 		data[i] = letoh16(val);
24594cd89cf2Sjmatthew 	}
24604cd89cf2Sjmatthew 
24614cd89cf2Sjmatthew 	csum = 0;
24624cd89cf2Sjmatthew 	for (i = 0; i < nitems(data); i++) {
24634cd89cf2Sjmatthew 		csum += data[i] & 0xff;
24644cd89cf2Sjmatthew 		csum += data[i] >> 8;
24654cd89cf2Sjmatthew 	}
24664cd89cf2Sjmatthew 
2467607c4deaSderaadt 	memcpy(&sc->sc_nvram, data, sizeof(sc->sc_nvram));
24684cd89cf2Sjmatthew 	/* id field should be 'ISP ', version should be at least 1 */
24694cd89cf2Sjmatthew 	if (sc->sc_nvram.id[0] != 'I' || sc->sc_nvram.id[1] != 'S' ||
24704cd89cf2Sjmatthew 	    sc->sc_nvram.id[2] != 'P' || sc->sc_nvram.id[3] != ' ' ||
24714cd89cf2Sjmatthew 	    sc->sc_nvram.nvram_version < 1 || (csum != 0)) {
24728e9b0352Skettenis 		/*
24738e9b0352Skettenis 		 * onboard 2200s on Sun hardware don't have an nvram
24748e9b0352Skettenis 		 * fitted, but will provide us with node and port name
24758e9b0352Skettenis 		 * through Open Firmware; don't complain in that case.
24768e9b0352Skettenis 		 */
24778e9b0352Skettenis 		if (sc->sc_node_name == 0 || sc->sc_port_name == 0)
24784cd89cf2Sjmatthew 			printf("%s: nvram corrupt\n", DEVNAME(sc));
24794cd89cf2Sjmatthew 		return (1);
24804cd89cf2Sjmatthew 	}
24814cd89cf2Sjmatthew 	return (0);
24824cd89cf2Sjmatthew }
24834cd89cf2Sjmatthew 
24844cd89cf2Sjmatthew struct qla_dmamem *
qla_dmamem_alloc(struct qla_softc * sc,size_t size)24854cd89cf2Sjmatthew qla_dmamem_alloc(struct qla_softc *sc, size_t size)
24864cd89cf2Sjmatthew {
24874cd89cf2Sjmatthew 	struct qla_dmamem *m;
24884cd89cf2Sjmatthew 	int nsegs;
24894cd89cf2Sjmatthew 
24904cd89cf2Sjmatthew 	m = malloc(sizeof(*m), M_DEVBUF, M_NOWAIT | M_ZERO);
24914cd89cf2Sjmatthew 	if (m == NULL)
24924cd89cf2Sjmatthew 		return (NULL);
24934cd89cf2Sjmatthew 
24944cd89cf2Sjmatthew 	m->qdm_size = size;
24954cd89cf2Sjmatthew 
24964cd89cf2Sjmatthew 	if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
24974cd89cf2Sjmatthew 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &m->qdm_map) != 0)
24984cd89cf2Sjmatthew 		goto qdmfree;
24994cd89cf2Sjmatthew 
25004cd89cf2Sjmatthew 	if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &m->qdm_seg, 1,
25014cd89cf2Sjmatthew 	    &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0)
25024cd89cf2Sjmatthew 		goto destroy;
25034cd89cf2Sjmatthew 
25044cd89cf2Sjmatthew 	if (bus_dmamem_map(sc->sc_dmat, &m->qdm_seg, nsegs, size, &m->qdm_kva,
25054cd89cf2Sjmatthew 	    BUS_DMA_NOWAIT) != 0)
25064cd89cf2Sjmatthew 		goto free;
25074cd89cf2Sjmatthew 
25084cd89cf2Sjmatthew 	if (bus_dmamap_load(sc->sc_dmat, m->qdm_map, m->qdm_kva, size, NULL,
25094cd89cf2Sjmatthew 	    BUS_DMA_NOWAIT) != 0)
25104cd89cf2Sjmatthew 		goto unmap;
25114cd89cf2Sjmatthew 
25124cd89cf2Sjmatthew 	return (m);
25134cd89cf2Sjmatthew 
25144cd89cf2Sjmatthew unmap:
25154cd89cf2Sjmatthew 	bus_dmamem_unmap(sc->sc_dmat, m->qdm_kva, m->qdm_size);
25164cd89cf2Sjmatthew free:
25174cd89cf2Sjmatthew 	bus_dmamem_free(sc->sc_dmat, &m->qdm_seg, 1);
25184cd89cf2Sjmatthew destroy:
25194cd89cf2Sjmatthew 	bus_dmamap_destroy(sc->sc_dmat, m->qdm_map);
25204cd89cf2Sjmatthew qdmfree:
2521bae2bd50Sderaadt 	free(m, M_DEVBUF, sizeof(*m));
25224cd89cf2Sjmatthew 
25234cd89cf2Sjmatthew 	return (NULL);
25244cd89cf2Sjmatthew }
25254cd89cf2Sjmatthew 
25264cd89cf2Sjmatthew void
qla_dmamem_free(struct qla_softc * sc,struct qla_dmamem * m)25274cd89cf2Sjmatthew qla_dmamem_free(struct qla_softc *sc, struct qla_dmamem *m)
25284cd89cf2Sjmatthew {
25294cd89cf2Sjmatthew 	bus_dmamap_unload(sc->sc_dmat, m->qdm_map);
25304cd89cf2Sjmatthew 	bus_dmamem_unmap(sc->sc_dmat, m->qdm_kva, m->qdm_size);
25314cd89cf2Sjmatthew 	bus_dmamem_free(sc->sc_dmat, &m->qdm_seg, 1);
25324cd89cf2Sjmatthew 	bus_dmamap_destroy(sc->sc_dmat, m->qdm_map);
2533bae2bd50Sderaadt 	free(m, M_DEVBUF, sizeof(*m));
25344cd89cf2Sjmatthew }
25354cd89cf2Sjmatthew 
25364cd89cf2Sjmatthew int
qla_alloc_ccbs(struct qla_softc * sc)25374cd89cf2Sjmatthew qla_alloc_ccbs(struct qla_softc *sc)
25384cd89cf2Sjmatthew {
25394cd89cf2Sjmatthew 	struct qla_ccb		*ccb;
25404cd89cf2Sjmatthew 	u_int8_t		*cmd;
25414cd89cf2Sjmatthew 	int			i;
25424cd89cf2Sjmatthew 
25434cd89cf2Sjmatthew 	SIMPLEQ_INIT(&sc->sc_ccb_free);
25444cd89cf2Sjmatthew 	mtx_init(&sc->sc_ccb_mtx, IPL_BIO);
25454cd89cf2Sjmatthew 	mtx_init(&sc->sc_queue_mtx, IPL_BIO);
25464cd89cf2Sjmatthew 	mtx_init(&sc->sc_port_mtx, IPL_BIO);
254786376500Sjmatthew 	mtx_init(&sc->sc_mbox_mtx, IPL_BIO);
25484cd89cf2Sjmatthew 
25499f6fb5c7Sderaadt 	sc->sc_ccbs = mallocarray(sc->sc_maxcmds, sizeof(struct qla_ccb),
25504cd89cf2Sjmatthew 	    M_DEVBUF, M_WAITOK | M_CANFAIL | M_ZERO);
25514cd89cf2Sjmatthew 	if (sc->sc_ccbs == NULL) {
25524cd89cf2Sjmatthew 		printf("%s: unable to allocate ccbs\n", DEVNAME(sc));
25534cd89cf2Sjmatthew 		return (1);
25544cd89cf2Sjmatthew 	}
25554cd89cf2Sjmatthew 
25564cd89cf2Sjmatthew 	sc->sc_requests = qla_dmamem_alloc(sc, sc->sc_maxcmds *
25574cd89cf2Sjmatthew 	    QLA_QUEUE_ENTRY_SIZE);
25584cd89cf2Sjmatthew 	if (sc->sc_requests == NULL) {
25594cd89cf2Sjmatthew 		printf("%s: unable to allocate ccb dmamem\n", DEVNAME(sc));
25604cd89cf2Sjmatthew 		goto free_ccbs;
25614cd89cf2Sjmatthew 	}
25624cd89cf2Sjmatthew 	sc->sc_responses = qla_dmamem_alloc(sc, sc->sc_maxcmds *
25634cd89cf2Sjmatthew 	    QLA_QUEUE_ENTRY_SIZE);
25644cd89cf2Sjmatthew 	if (sc->sc_responses == NULL) {
25654cd89cf2Sjmatthew 		printf("%s: unable to allocate rcb dmamem\n", DEVNAME(sc));
25664cd89cf2Sjmatthew 		goto free_req;
25674cd89cf2Sjmatthew 	}
25684cd89cf2Sjmatthew 	sc->sc_segments = qla_dmamem_alloc(sc, sc->sc_maxcmds * QLA_MAX_SEGS *
25694cd89cf2Sjmatthew 	    sizeof(struct qla_iocb_seg));
25704cd89cf2Sjmatthew 	if (sc->sc_segments == NULL) {
25714cd89cf2Sjmatthew 		printf("%s: unable to allocate iocb segments\n", DEVNAME(sc));
25724cd89cf2Sjmatthew 		goto free_res;
25734cd89cf2Sjmatthew 	}
25744cd89cf2Sjmatthew 
25754cd89cf2Sjmatthew 	cmd = QLA_DMA_KVA(sc->sc_requests);
25764cd89cf2Sjmatthew 	memset(cmd, 0, QLA_QUEUE_ENTRY_SIZE * sc->sc_maxcmds);
25774cd89cf2Sjmatthew 	for (i = 0; i < sc->sc_maxcmds; i++) {
25784cd89cf2Sjmatthew 		ccb = &sc->sc_ccbs[i];
25794cd89cf2Sjmatthew 
25804cd89cf2Sjmatthew 		if (bus_dmamap_create(sc->sc_dmat, MAXPHYS,
25814cd89cf2Sjmatthew 		    QLA_MAX_SEGS, MAXPHYS, 0,
25824cd89cf2Sjmatthew 		    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
25834cd89cf2Sjmatthew 		    &ccb->ccb_dmamap) != 0) {
25844cd89cf2Sjmatthew 			printf("%s: unable to create dma map\n", DEVNAME(sc));
25854cd89cf2Sjmatthew 			goto free_maps;
25864cd89cf2Sjmatthew 		}
25874cd89cf2Sjmatthew 
25884cd89cf2Sjmatthew 		ccb->ccb_sc = sc;
25894cd89cf2Sjmatthew 		ccb->ccb_id = i;
25904cd89cf2Sjmatthew 
25914cd89cf2Sjmatthew 		ccb->ccb_seg_offset = i * QLA_MAX_SEGS *
25924cd89cf2Sjmatthew 		    sizeof(struct qla_iocb_seg);
259391637d79Sdlg 		htolem64(&ccb->ccb_seg_dva,
259491637d79Sdlg 		    QLA_DMA_DVA(sc->sc_segments) + ccb->ccb_seg_offset);
25954cd89cf2Sjmatthew 		ccb->ccb_t4segs = QLA_DMA_KVA(sc->sc_segments) +
25964cd89cf2Sjmatthew 		    ccb->ccb_seg_offset;
25974cd89cf2Sjmatthew 
25984cd89cf2Sjmatthew 		qla_put_ccb(sc, ccb);
25994cd89cf2Sjmatthew 	}
26004cd89cf2Sjmatthew 
26014cd89cf2Sjmatthew 	scsi_iopool_init(&sc->sc_iopool, sc, qla_get_ccb, qla_put_ccb);
26024cd89cf2Sjmatthew 	return (0);
26034cd89cf2Sjmatthew 
26044cd89cf2Sjmatthew free_maps:
26054cd89cf2Sjmatthew 	while ((ccb = qla_get_ccb(sc)) != NULL)
26064cd89cf2Sjmatthew 		bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
26074cd89cf2Sjmatthew 
26084cd89cf2Sjmatthew 	qla_dmamem_free(sc, sc->sc_segments);
26094cd89cf2Sjmatthew free_res:
26104cd89cf2Sjmatthew 	qla_dmamem_free(sc, sc->sc_responses);
26114cd89cf2Sjmatthew free_req:
26124cd89cf2Sjmatthew 	qla_dmamem_free(sc, sc->sc_requests);
26134cd89cf2Sjmatthew free_ccbs:
2614aa3cabd0Stedu 	free(sc->sc_ccbs, M_DEVBUF, 0);
26154cd89cf2Sjmatthew 
26164cd89cf2Sjmatthew 	return (1);
26174cd89cf2Sjmatthew }
26184cd89cf2Sjmatthew 
26194cd89cf2Sjmatthew void
qla_free_ccbs(struct qla_softc * sc)26204cd89cf2Sjmatthew qla_free_ccbs(struct qla_softc *sc)
26214cd89cf2Sjmatthew {
26224cd89cf2Sjmatthew 	struct qla_ccb		*ccb;
26234cd89cf2Sjmatthew 
26244cd89cf2Sjmatthew 	scsi_iopool_destroy(&sc->sc_iopool);
26254cd89cf2Sjmatthew 	while ((ccb = qla_get_ccb(sc)) != NULL)
26264cd89cf2Sjmatthew 		bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
26274cd89cf2Sjmatthew 	qla_dmamem_free(sc, sc->sc_segments);
26284cd89cf2Sjmatthew 	qla_dmamem_free(sc, sc->sc_responses);
26294cd89cf2Sjmatthew 	qla_dmamem_free(sc, sc->sc_requests);
2630aa3cabd0Stedu 	free(sc->sc_ccbs, M_DEVBUF, 0);
26314cd89cf2Sjmatthew }
26324cd89cf2Sjmatthew 
26334cd89cf2Sjmatthew void *
qla_get_ccb(void * xsc)26344cd89cf2Sjmatthew qla_get_ccb(void *xsc)
26354cd89cf2Sjmatthew {
26364cd89cf2Sjmatthew 	struct qla_softc	*sc = xsc;
26374cd89cf2Sjmatthew 	struct qla_ccb		*ccb;
26384cd89cf2Sjmatthew 
26394cd89cf2Sjmatthew 	mtx_enter(&sc->sc_ccb_mtx);
26404cd89cf2Sjmatthew 	ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free);
26414cd89cf2Sjmatthew 	if (ccb != NULL) {
26424cd89cf2Sjmatthew 		SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb_link);
26434cd89cf2Sjmatthew 	}
26444cd89cf2Sjmatthew 	mtx_leave(&sc->sc_ccb_mtx);
26454cd89cf2Sjmatthew 	return (ccb);
26464cd89cf2Sjmatthew }
26474cd89cf2Sjmatthew 
26484cd89cf2Sjmatthew void
qla_put_ccb(void * xsc,void * io)26494cd89cf2Sjmatthew qla_put_ccb(void *xsc, void *io)
26504cd89cf2Sjmatthew {
26514cd89cf2Sjmatthew 	struct qla_softc	*sc = xsc;
26524cd89cf2Sjmatthew 	struct qla_ccb		*ccb = io;
26534cd89cf2Sjmatthew 
26544cd89cf2Sjmatthew 	ccb->ccb_xs = NULL;
26554cd89cf2Sjmatthew 	mtx_enter(&sc->sc_ccb_mtx);
26564cd89cf2Sjmatthew 	SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_link);
26574cd89cf2Sjmatthew 	mtx_leave(&sc->sc_ccb_mtx);
26584cd89cf2Sjmatthew }
2659