xref: /openbsd-src/sys/dev/pci/qle.c (revision 9593dc34da13a12012033a17061c846c208061c2)
1*9593dc34Smglocker /*	$OpenBSD: qle.c,v 1.66 2024/09/04 07:54:52 mglocker Exp $ */
23759911cSjmatthew 
33759911cSjmatthew /*
43759911cSjmatthew  * Copyright (c) 2013, 2014 Jonathan Matthew <jmatthew@openbsd.org>
53759911cSjmatthew  *
63759911cSjmatthew  * Permission to use, copy, modify, and distribute this software for any
73759911cSjmatthew  * purpose with or without fee is hereby granted, provided that the above
83759911cSjmatthew  * copyright notice and this permission notice appear in all copies.
93759911cSjmatthew  *
103759911cSjmatthew  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113759911cSjmatthew  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123759911cSjmatthew  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133759911cSjmatthew  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143759911cSjmatthew  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153759911cSjmatthew  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163759911cSjmatthew  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173759911cSjmatthew  */
183759911cSjmatthew 
193759911cSjmatthew #include <sys/param.h>
203759911cSjmatthew #include <sys/systm.h>
219a1bdb58Svisa #include <sys/atomic.h>
223759911cSjmatthew #include <sys/malloc.h>
233759911cSjmatthew #include <sys/device.h>
243759911cSjmatthew #include <sys/task.h>
253fc6fae1Sjmatthew #include <sys/timeout.h>
263759911cSjmatthew 
273759911cSjmatthew #include <machine/bus.h>
283759911cSjmatthew 
293759911cSjmatthew #include <dev/pci/pcireg.h>
303759911cSjmatthew #include <dev/pci/pcivar.h>
313759911cSjmatthew #include <dev/pci/pcidevs.h>
323759911cSjmatthew 
333759911cSjmatthew #include <scsi/scsi_all.h>
343759911cSjmatthew #include <scsi/scsiconf.h>
353759911cSjmatthew 
363759911cSjmatthew #include <dev/pci/qlereg.h>
373759911cSjmatthew 
38cc014dc0Sjmatthew #ifdef QLE_DEBUG
39cc014dc0Sjmatthew #define DPRINTF(m, f...) do { if ((qledebug & (m)) == (m)) printf(f); } \
40cc014dc0Sjmatthew     while (0)
41cc014dc0Sjmatthew #define QLE_D_MBOX		0x01
42cc014dc0Sjmatthew #define QLE_D_INTR		0x02
43cc014dc0Sjmatthew #define QLE_D_PORT		0x04
44cc014dc0Sjmatthew #define QLE_D_IO		0x08
45cc014dc0Sjmatthew #define QLE_D_IOCB		0x10
46cc014dc0Sjmatthew int qledebug = QLE_D_PORT;
47cc014dc0Sjmatthew #else
48cc014dc0Sjmatthew #define DPRINTF(m, f...)
49cc014dc0Sjmatthew #endif
50cc014dc0Sjmatthew 
51c8005c8bSjmatthew #ifndef QLE_NOFIRMWARE
523759911cSjmatthew #include <dev/microcode/isp/asm_2400.h>
53b803df0bSjmatthew #include <dev/microcode/isp/asm_2500.h>
54989131ebSjmatthew #endif
553759911cSjmatthew 
563759911cSjmatthew #define QLE_PCI_MEM_BAR		0x14
573759911cSjmatthew #define QLE_PCI_IO_BAR		0x10
583759911cSjmatthew 
593759911cSjmatthew 
603759911cSjmatthew #define QLE_DEFAULT_PORT_NAME		0x400000007F000003ULL /* from isp(4) */
613759911cSjmatthew 
6211e354acSjmatthew #define QLE_WAIT_FOR_LOOP		10	/* seconds */
6311e354acSjmatthew #define QLE_LOOP_SETTLE			200	/* ms */
643759911cSjmatthew 
653759911cSjmatthew /* rounded up range of assignable handles */
663759911cSjmatthew #define QLE_MAX_TARGETS			2048
673759911cSjmatthew 
683759911cSjmatthew /* maximum number of segments allowed for in a single io */
6920c4379eSjmatthew #define QLE_MAX_SEGS			32
703759911cSjmatthew 
713759911cSjmatthew enum qle_isp_gen {
723759911cSjmatthew 	QLE_GEN_ISP24XX = 1,
73b803df0bSjmatthew 	QLE_GEN_ISP25XX
743759911cSjmatthew };
753759911cSjmatthew 
763759911cSjmatthew enum qle_isp_type {
773759911cSjmatthew 	QLE_ISP2422 = 1,
783759911cSjmatthew 	QLE_ISP2432,
793759911cSjmatthew 	QLE_ISP2512,
803759911cSjmatthew 	QLE_ISP2522,
813759911cSjmatthew 	QLE_ISP2532
823759911cSjmatthew };
833759911cSjmatthew 
843759911cSjmatthew /* port database things */
853759911cSjmatthew #define QLE_SCRATCH_SIZE		0x1000
863759911cSjmatthew 
873759911cSjmatthew enum qle_port_disp {
883759911cSjmatthew 	QLE_PORT_DISP_NEW,
893759911cSjmatthew 	QLE_PORT_DISP_GONE,
903759911cSjmatthew 	QLE_PORT_DISP_SAME,
913759911cSjmatthew 	QLE_PORT_DISP_CHANGED,
923759911cSjmatthew 	QLE_PORT_DISP_MOVED,
933759911cSjmatthew 	QLE_PORT_DISP_DUP
943759911cSjmatthew };
953759911cSjmatthew 
963759911cSjmatthew #define QLE_LOCATION_LOOP		(1 << 24)
973759911cSjmatthew #define QLE_LOCATION_FABRIC		(2 << 24)
983759911cSjmatthew #define QLE_LOCATION_LOOP_ID(l)		(l | QLE_LOCATION_LOOP)
993759911cSjmatthew #define QLE_LOCATION_PORT_ID(p)		(p | QLE_LOCATION_FABRIC)
1003759911cSjmatthew 
1013759911cSjmatthew struct qle_fc_port {
1023759911cSjmatthew 	TAILQ_ENTRY(qle_fc_port) ports;
1033759911cSjmatthew 	TAILQ_ENTRY(qle_fc_port) update;
1043759911cSjmatthew 
1053759911cSjmatthew 	u_int64_t	node_name;
1063759911cSjmatthew 	u_int64_t	port_name;
1073759911cSjmatthew 	u_int32_t	location;	/* port id or loop id */
1083759911cSjmatthew 
1093759911cSjmatthew 	int		flags;
1103759911cSjmatthew #define QLE_PORT_FLAG_IS_TARGET		1
1113759911cSjmatthew #define QLE_PORT_FLAG_NEEDS_LOGIN	2
1123759911cSjmatthew 
1133759911cSjmatthew 	u_int32_t	portid;
1143759911cSjmatthew 	u_int16_t	loopid;
1153759911cSjmatthew };
1163759911cSjmatthew 
1173759911cSjmatthew 
1183759911cSjmatthew /* request/response queue stuff */
1193759911cSjmatthew #define QLE_QUEUE_ENTRY_SIZE		64
1203759911cSjmatthew 
1213759911cSjmatthew struct qle_ccb {
1223759911cSjmatthew 	struct qle_softc 	*ccb_sc;
1233759911cSjmatthew 	int			ccb_id;
1243759911cSjmatthew 	struct scsi_xfer	*ccb_xs;
1253759911cSjmatthew 
1263759911cSjmatthew 	bus_dmamap_t		ccb_dmamap;
1273759911cSjmatthew 
1283759911cSjmatthew 	struct qle_iocb_seg	*ccb_segs;
1293759911cSjmatthew 	u_int64_t		ccb_seg_offset;
1303759911cSjmatthew 
1313759911cSjmatthew 	SIMPLEQ_ENTRY(qle_ccb)	ccb_link;
1323759911cSjmatthew };
1333759911cSjmatthew 
1343759911cSjmatthew SIMPLEQ_HEAD(qle_ccb_list, qle_ccb);
1353759911cSjmatthew 
1363759911cSjmatthew struct qle_dmamem {
1373759911cSjmatthew 	bus_dmamap_t		qdm_map;
1383759911cSjmatthew 	bus_dma_segment_t	qdm_seg;
1393759911cSjmatthew 	size_t			qdm_size;
1403759911cSjmatthew 	caddr_t			qdm_kva;
1413759911cSjmatthew };
1423759911cSjmatthew #define QLE_DMA_MAP(_qdm)	((_qdm)->qdm_map)
1433759911cSjmatthew #define QLE_DMA_LEN(_qdm)	((_qdm)->qdm_size)
1443759911cSjmatthew #define QLE_DMA_DVA(_qdm)	((u_int64_t)(_qdm)->qdm_map->dm_segs[0].ds_addr)
1453759911cSjmatthew #define QLE_DMA_KVA(_qdm)	((void *)(_qdm)->qdm_kva)
1463759911cSjmatthew 
1473759911cSjmatthew struct qle_softc {
1483759911cSjmatthew 	struct device		sc_dev;
1493759911cSjmatthew 
1503759911cSjmatthew 	pci_chipset_tag_t	sc_pc;
1513759911cSjmatthew 	pcitag_t		sc_tag;
1523759911cSjmatthew 
1533759911cSjmatthew 	void			*sc_ih;
1543759911cSjmatthew 	bus_space_tag_t		sc_iot;
1553759911cSjmatthew 	bus_space_handle_t	sc_ioh;
1563759911cSjmatthew 	bus_size_t		sc_ios;
1573759911cSjmatthew 	bus_dma_tag_t		sc_dmat;
1583759911cSjmatthew 
1593759911cSjmatthew 	struct scsibus_softc	*sc_scsibus;
1603759911cSjmatthew 
1613759911cSjmatthew 	enum qle_isp_type	sc_isp_type;
1623759911cSjmatthew 	enum qle_isp_gen	sc_isp_gen;
1633759911cSjmatthew 	int			sc_port;
1643759911cSjmatthew 
16569e488dfSdlg 	bus_space_handle_t	sc_mbox_ioh;
1663759911cSjmatthew 	u_int16_t		sc_mbox[QLE_MBOX_COUNT];
1673759911cSjmatthew 	int			sc_mbox_pending;
1683759911cSjmatthew 	struct mutex		sc_mbox_mtx;
1693759911cSjmatthew 
1703759911cSjmatthew 	int			sc_loop_up;
1713759911cSjmatthew 	int			sc_topology;
1723759911cSjmatthew 	int			sc_loop_id;
1733759911cSjmatthew 	int			sc_port_id;
1743759911cSjmatthew 	int			sc_loop_max_id;
1753759911cSjmatthew 	u_int64_t		sc_sns_port_name;
1763759911cSjmatthew 
1773759911cSjmatthew 	struct mutex		sc_port_mtx;
1783759911cSjmatthew 	TAILQ_HEAD(, qle_fc_port) sc_ports;
1793759911cSjmatthew 	TAILQ_HEAD(, qle_fc_port) sc_ports_new;
1803759911cSjmatthew 	TAILQ_HEAD(, qle_fc_port) sc_ports_gone;
1813759911cSjmatthew 	TAILQ_HEAD(, qle_fc_port) sc_ports_found;
1823759911cSjmatthew 	struct qle_fc_port	*sc_targets[QLE_MAX_TARGETS];
1833759911cSjmatthew 
1843759911cSjmatthew 	struct taskq		*sc_update_taskq;
1853759911cSjmatthew 	struct task		sc_update_task;
1863fc6fae1Sjmatthew 	struct timeout		sc_update_timeout;
1873759911cSjmatthew 	int			sc_update;
1883759911cSjmatthew 	int			sc_update_tasks;
1893759911cSjmatthew #define	QLE_UPDATE_TASK_CLEAR_ALL	0x00000001
1903759911cSjmatthew #define QLE_UPDATE_TASK_SOFTRESET	0x00000002
191580dbe8fSjmatthew #define QLE_UPDATE_TASK_UPDATE_TOPO	0x00000004
192580dbe8fSjmatthew #define QLE_UPDATE_TASK_GET_PORT_LIST	0x00000008
193580dbe8fSjmatthew #define QLE_UPDATE_TASK_PORT_LIST	0x00000010
194580dbe8fSjmatthew #define QLE_UPDATE_TASK_SCAN_FABRIC	0x00000020
195580dbe8fSjmatthew #define QLE_UPDATE_TASK_SCANNING_FABRIC	0x00000040
196580dbe8fSjmatthew #define QLE_UPDATE_TASK_FABRIC_LOGIN	0x00000080
197580dbe8fSjmatthew #define QLE_UPDATE_TASK_FABRIC_RELOGIN	0x00000100
198580dbe8fSjmatthew #define QLE_UPDATE_TASK_DETACH_TARGET	0x00000200
199580dbe8fSjmatthew #define QLE_UPDATE_TASK_ATTACH_TARGET	0x00000400
2003759911cSjmatthew 
2013759911cSjmatthew 	int			sc_maxcmds;
2023759911cSjmatthew 	struct qle_dmamem	*sc_requests;
2033759911cSjmatthew 	struct qle_dmamem	*sc_responses;
2043759911cSjmatthew 	struct qle_dmamem	*sc_segments;
2053759911cSjmatthew 	struct qle_dmamem	*sc_pri_requests;
2063759911cSjmatthew 	struct qle_dmamem	*sc_scratch;
2073759911cSjmatthew 	struct qle_dmamem	*sc_fcp_cmnds;
2083759911cSjmatthew 	struct qle_ccb		*sc_ccbs;
2093759911cSjmatthew 	struct qle_ccb_list	sc_ccb_free;
2103759911cSjmatthew 	struct mutex		sc_ccb_mtx;
2113759911cSjmatthew 	struct mutex		sc_queue_mtx;
2123759911cSjmatthew 	struct scsi_iopool	sc_iopool;
2133759911cSjmatthew 	u_int32_t		sc_next_req_id;
2143759911cSjmatthew 	u_int32_t		sc_last_resp_id;
2153759911cSjmatthew 	int			sc_marker_required;
2163759911cSjmatthew 	int			sc_fabric_pending;
217e5d3c726Sjmatthew 	u_int8_t		sc_fabric_response[QLE_QUEUE_ENTRY_SIZE];
2183759911cSjmatthew 
2193759911cSjmatthew 	struct qle_nvram	sc_nvram;
2203759911cSjmatthew 	int			sc_nvram_valid;
2213759911cSjmatthew };
2223759911cSjmatthew #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
2233759911cSjmatthew 
2243759911cSjmatthew int	qle_intr(void *);
2253759911cSjmatthew 
2263759911cSjmatthew int	qle_match(struct device *, void *, void *);
2273759911cSjmatthew void	qle_attach(struct device *, struct device *, void *);
2283759911cSjmatthew int	qle_detach(struct device *, int);
2293759911cSjmatthew 
2308d2c75e4Smpi const struct cfattach qle_ca = {
2313759911cSjmatthew 	sizeof(struct qle_softc),
2323759911cSjmatthew 	qle_match,
2333759911cSjmatthew 	qle_attach,
2343759911cSjmatthew 	qle_detach
2353759911cSjmatthew };
2363759911cSjmatthew 
2373759911cSjmatthew struct cfdriver qle_cd = {
2383759911cSjmatthew 	NULL,
2393759911cSjmatthew 	"qle",
2403759911cSjmatthew 	DV_DULL
2413759911cSjmatthew };
2423759911cSjmatthew 
2433759911cSjmatthew void		qle_scsi_cmd(struct scsi_xfer *);
2443759911cSjmatthew int		qle_scsi_probe(struct scsi_link *);
2453759911cSjmatthew 
2463759911cSjmatthew 
247a454aff3Snaddy const struct scsi_adapter qle_switch = {
24821ceeee0Skrw 	qle_scsi_cmd, NULL, qle_scsi_probe, NULL, NULL
2493759911cSjmatthew };
2503759911cSjmatthew 
2513759911cSjmatthew u_int32_t	qle_read(struct qle_softc *, int);
2523759911cSjmatthew void		qle_write(struct qle_softc *, int, u_int32_t);
2533759911cSjmatthew void		qle_host_cmd(struct qle_softc *sc, u_int32_t);
2543759911cSjmatthew 
25565fd2507Sjmatthew int		qle_mbox(struct qle_softc *, int);
2563759911cSjmatthew int		qle_ct_pass_through(struct qle_softc *sc,
2573759911cSjmatthew 		    u_int32_t port_handle, struct qle_dmamem *mem,
2583759911cSjmatthew 		    size_t req_size, size_t resp_size);
2593759911cSjmatthew void		qle_mbox_putaddr(u_int16_t *, struct qle_dmamem *);
2603759911cSjmatthew u_int16_t	qle_read_mbox(struct qle_softc *, int);
2613759911cSjmatthew void		qle_write_mbox(struct qle_softc *, int, u_int16_t);
2623759911cSjmatthew 
2633759911cSjmatthew void		qle_handle_intr(struct qle_softc *, u_int16_t, u_int16_t);
2643759911cSjmatthew void		qle_set_ints(struct qle_softc *, int);
2653759911cSjmatthew int		qle_read_isr(struct qle_softc *, u_int16_t *, u_int16_t *);
2663759911cSjmatthew void		qle_clear_isr(struct qle_softc *, u_int16_t);
2673759911cSjmatthew 
2683759911cSjmatthew void		qle_put_marker(struct qle_softc *, void *);
2693759911cSjmatthew void		qle_put_cmd(struct qle_softc *, void *, struct scsi_xfer *,
2703759911cSjmatthew 		    struct qle_ccb *, u_int32_t);
271ce28b8eaSdlg struct qle_ccb *qle_handle_resp(struct qle_softc *, u_int32_t);
272a0abcb68Sdlg void		qle_sge(struct qle_iocb_seg *, u_int64_t, u_int32_t);
2733759911cSjmatthew 
2743759911cSjmatthew struct qle_fc_port *qle_next_fabric_port(struct qle_softc *, u_int32_t *,
2753759911cSjmatthew 		    u_int32_t *);
2763759911cSjmatthew int		qle_get_port_db(struct qle_softc *, u_int16_t,
2773759911cSjmatthew 		    struct qle_dmamem *);
278580dbe8fSjmatthew int		qle_get_port_name_list(struct qle_softc *sc, u_int32_t);
279580dbe8fSjmatthew int		qle_add_loop_port(struct qle_softc *, struct qle_fc_port *);
2803759911cSjmatthew int		qle_add_fabric_port(struct qle_softc *, struct qle_fc_port *);
2810d6eccfaSjmatthew int		qle_add_logged_in_port(struct qle_softc *, u_int16_t,
282b9154820Sjmatthew 		    u_int32_t);
2833759911cSjmatthew int		qle_classify_port(struct qle_softc *, u_int32_t, u_int64_t,
2843759911cSjmatthew 		    u_int64_t, struct qle_fc_port **);
2850d6eccfaSjmatthew int		qle_get_loop_id(struct qle_softc *sc, int);
2863759911cSjmatthew void		qle_clear_port_lists(struct qle_softc *);
2873759911cSjmatthew int		qle_softreset(struct qle_softc *);
2883759911cSjmatthew void		qle_update_topology(struct qle_softc *);
2893759911cSjmatthew int		qle_update_fabric(struct qle_softc *);
290e5d3c726Sjmatthew int		qle_fabric_plogx(struct qle_softc *, struct qle_fc_port *, int,
291e5d3c726Sjmatthew 		    u_int32_t *);
2923759911cSjmatthew int		qle_fabric_plogi(struct qle_softc *, struct qle_fc_port *);
2933759911cSjmatthew void		qle_fabric_plogo(struct qle_softc *, struct qle_fc_port *);
2943759911cSjmatthew 
2953759911cSjmatthew void		qle_update_start(struct qle_softc *, int);
2963fc6fae1Sjmatthew void		qle_update_defer(struct qle_softc *, int);
2973fc6fae1Sjmatthew void		qle_update_cancel(struct qle_softc *);
2983759911cSjmatthew void		qle_update_done(struct qle_softc *, int);
299e4195480Sdlg void		qle_do_update(void *);
3003fc6fae1Sjmatthew void		qle_deferred_update(void *);
3013759911cSjmatthew int		qle_async(struct qle_softc *, u_int16_t);
3023759911cSjmatthew 
303b803df0bSjmatthew int		qle_load_fwchunk(struct qle_softc *,
3043759911cSjmatthew 		    struct qle_dmamem *, const u_int32_t *);
305c8005c8bSjmatthew u_int32_t	qle_read_ram_word(struct qle_softc *, u_int32_t);
306c8005c8bSjmatthew int		qle_verify_firmware(struct qle_softc *, u_int32_t);
307b803df0bSjmatthew int		qle_load_firmware_chunks(struct qle_softc *, const u_int32_t *);
3083759911cSjmatthew int		qle_read_nvram(struct qle_softc *);
3093759911cSjmatthew 
3103759911cSjmatthew struct qle_dmamem *qle_dmamem_alloc(struct qle_softc *, size_t);
3113759911cSjmatthew void		qle_dmamem_free(struct qle_softc *, struct qle_dmamem *);
3123759911cSjmatthew 
3133759911cSjmatthew int		qle_alloc_ccbs(struct qle_softc *);
3143759911cSjmatthew void		qle_free_ccbs(struct qle_softc *);
3153759911cSjmatthew void		*qle_get_ccb(void *);
3163759911cSjmatthew void		qle_put_ccb(void *, void *);
3173759911cSjmatthew 
3183759911cSjmatthew void		qle_dump_stuff(struct qle_softc *, void *, int);
3193759911cSjmatthew void		qle_dump_iocb(struct qle_softc *, void *);
3203759911cSjmatthew void		qle_dump_iocb_segs(struct qle_softc *, void *, int);
3213759911cSjmatthew 
3223759911cSjmatthew static const struct pci_matchid qle_devices[] = {
3233759911cSjmatthew 	{ PCI_VENDOR_QLOGIC,	PCI_PRODUCT_QLOGIC_ISP2422 },
3243759911cSjmatthew 	{ PCI_VENDOR_QLOGIC,	PCI_PRODUCT_QLOGIC_ISP2432 },
325b803df0bSjmatthew 	{ PCI_VENDOR_QLOGIC,	PCI_PRODUCT_QLOGIC_ISP2512 },
3263759911cSjmatthew 	{ PCI_VENDOR_QLOGIC,	PCI_PRODUCT_QLOGIC_ISP2522 },
327b803df0bSjmatthew 	{ PCI_VENDOR_QLOGIC,	PCI_PRODUCT_QLOGIC_ISP2532 },
3283759911cSjmatthew };
3293759911cSjmatthew 
3303759911cSjmatthew int
3313759911cSjmatthew qle_match(struct device *parent, void *match, void *aux)
3323759911cSjmatthew {
333b803df0bSjmatthew 	return (pci_matchbyid(aux, qle_devices, nitems(qle_devices)));
3343759911cSjmatthew }
3353759911cSjmatthew 
3363759911cSjmatthew void
3373759911cSjmatthew qle_attach(struct device *parent, struct device *self, void *aux)
3383759911cSjmatthew {
3393759911cSjmatthew 	struct qle_softc *sc = (void *)self;
3403759911cSjmatthew 	struct pci_attach_args *pa = aux;
3413759911cSjmatthew 	pci_intr_handle_t ih;
342ba6d46bdSkettenis 	const char *intrstr;
3433759911cSjmatthew 	u_int32_t pcictl;
3443759911cSjmatthew 	struct scsibus_attach_args saa;
3453759911cSjmatthew 	struct qle_init_cb *icb;
34669e488dfSdlg 	bus_size_t mbox_base;
347c8005c8bSjmatthew 	u_int32_t firmware_addr;
348c8005c8bSjmatthew #ifndef QLE_NOFIRMWARE
349c8005c8bSjmatthew 	const u_int32_t *firmware = NULL;
350c8005c8bSjmatthew #endif
3513759911cSjmatthew 
3523759911cSjmatthew 	pcireg_t bars[] = { QLE_PCI_MEM_BAR, QLE_PCI_IO_BAR };
3533759911cSjmatthew 	pcireg_t memtype;
35432c7aa28Sjsg 	int r, i, rv, loop_up = 0;
3553759911cSjmatthew 
3563759911cSjmatthew 	sc->sc_pc = pa->pa_pc;
3573759911cSjmatthew 	sc->sc_tag = pa->pa_tag;
3583759911cSjmatthew 	sc->sc_ih = NULL;
3593759911cSjmatthew 	sc->sc_dmat = pa->pa_dmat;
3603759911cSjmatthew 	sc->sc_ios = 0;
3613759911cSjmatthew 
3623759911cSjmatthew 	for (r = 0; r < nitems(bars); r++) {
3633759911cSjmatthew 		memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, bars[r]);
3643759911cSjmatthew 		if (pci_mapreg_map(pa, bars[r], memtype, 0,
3653759911cSjmatthew 		    &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_ios, 0) == 0)
3663759911cSjmatthew 			break;
3673759911cSjmatthew 
3683759911cSjmatthew 		sc->sc_ios = 0;
3693759911cSjmatthew 	}
3703759911cSjmatthew 	if (sc->sc_ios == 0) {
3713759911cSjmatthew 		printf(": unable to map registers\n");
3723759911cSjmatthew 		return;
3733759911cSjmatthew 	}
3743759911cSjmatthew 
37531ccda6aSdlg 	if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) {
3763759911cSjmatthew 		printf(": unable to map interrupt\n");
3773759911cSjmatthew 		goto unmap;
3783759911cSjmatthew 	}
379ba6d46bdSkettenis 	intrstr = pci_intr_string(sc->sc_pc, ih);
3803759911cSjmatthew 	sc->sc_ih = pci_intr_establish(sc->sc_pc, ih, IPL_BIO,
38143d6dcd9Sdlg 	    qle_intr, sc, DEVNAME(sc));
3823759911cSjmatthew 	if (sc->sc_ih == NULL) {
383ba6d46bdSkettenis 		printf(": unable to establish interrupt");
384ba6d46bdSkettenis 		if (intrstr != NULL)
385ba6d46bdSkettenis 			printf(" at %s", intrstr);
386ba6d46bdSkettenis 		printf("\n");
3873759911cSjmatthew 		goto deintr;
3883759911cSjmatthew 	}
3893759911cSjmatthew 
390ba6d46bdSkettenis 	printf(": %s\n", intrstr);
391ba6d46bdSkettenis 
3923759911cSjmatthew 	pcictl = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
3933759911cSjmatthew 	pcictl |= PCI_COMMAND_INVALIDATE_ENABLE |
3943759911cSjmatthew 	    PCI_COMMAND_PARITY_ENABLE | PCI_COMMAND_SERR_ENABLE;
3953759911cSjmatthew 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, pcictl);
3963759911cSjmatthew 
3973759911cSjmatthew 	pcictl = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
3983759911cSjmatthew 	pcictl &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
3993759911cSjmatthew 	pcictl &= ~(PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT);
4003759911cSjmatthew 	pcictl |= (0x80 << PCI_LATTIMER_SHIFT);
4013759911cSjmatthew 	pcictl |= (0x10 << PCI_CACHELINE_SHIFT);
4023759911cSjmatthew 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, pcictl);
4033759911cSjmatthew 
4043759911cSjmatthew 	pcictl = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG);
4053759911cSjmatthew 	pcictl &= ~1;
4063759911cSjmatthew 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, pcictl);
4073759911cSjmatthew 
4083759911cSjmatthew 	switch (PCI_PRODUCT(pa->pa_id)) {
4093759911cSjmatthew 	case PCI_PRODUCT_QLOGIC_ISP2422:
4103759911cSjmatthew 		sc->sc_isp_type = QLE_ISP2422;
4113759911cSjmatthew 		sc->sc_isp_gen = QLE_GEN_ISP24XX;
4123759911cSjmatthew 		break;
4133759911cSjmatthew 	case PCI_PRODUCT_QLOGIC_ISP2432:
4143759911cSjmatthew 		sc->sc_isp_type = QLE_ISP2432;
4153759911cSjmatthew 		sc->sc_isp_gen = QLE_GEN_ISP24XX;
4163759911cSjmatthew 		break;
4173759911cSjmatthew 	case PCI_PRODUCT_QLOGIC_ISP2512:
4183759911cSjmatthew 		sc->sc_isp_type = QLE_ISP2512;
4193759911cSjmatthew 		sc->sc_isp_gen = QLE_GEN_ISP25XX;
4203759911cSjmatthew 		break;
4213759911cSjmatthew 	case PCI_PRODUCT_QLOGIC_ISP2522:
4223759911cSjmatthew 		sc->sc_isp_type = QLE_ISP2522;
4233759911cSjmatthew 		sc->sc_isp_gen = QLE_GEN_ISP25XX;
4243759911cSjmatthew 		break;
4253759911cSjmatthew 	case PCI_PRODUCT_QLOGIC_ISP2532:
4263759911cSjmatthew 		sc->sc_isp_type = QLE_ISP2532;
4273759911cSjmatthew 		sc->sc_isp_gen = QLE_GEN_ISP25XX;
4283759911cSjmatthew 		break;
4293759911cSjmatthew 
4303759911cSjmatthew 	default:
4313759911cSjmatthew 		printf("unknown pci id %x", pa->pa_id);
4323759911cSjmatthew 		goto deintr;
4333759911cSjmatthew 	}
4343759911cSjmatthew 
435c8005c8bSjmatthew 	/* these are the same for 24xx and 25xx but may vary later */
436c8005c8bSjmatthew 	mbox_base = QLE_MBOX_BASE_24XX;
437c8005c8bSjmatthew 	firmware_addr = QLE_2400_CODE_ORG;
438c8005c8bSjmatthew 
43969e488dfSdlg 	if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, mbox_base,
44069e488dfSdlg 	    sizeof(sc->sc_mbox), &sc->sc_mbox_ioh) != 0) {
44169e488dfSdlg 		printf("%s: unable to map mbox registers\n", DEVNAME(sc));
44269e488dfSdlg 		goto deintr;
44369e488dfSdlg 	}
44469e488dfSdlg 
4453759911cSjmatthew 	sc->sc_port = pa->pa_function;
4463759911cSjmatthew 
4473759911cSjmatthew 	TAILQ_INIT(&sc->sc_ports);
4483759911cSjmatthew 	TAILQ_INIT(&sc->sc_ports_new);
4493759911cSjmatthew 	TAILQ_INIT(&sc->sc_ports_gone);
4503759911cSjmatthew 	TAILQ_INIT(&sc->sc_ports_found);
4513759911cSjmatthew 
4523759911cSjmatthew 	/* after reset, mbox regs 1 and 2 contain the string "ISP " */
4533759911cSjmatthew 	if (qle_read_mbox(sc, 1) != 0x4953 ||
4543759911cSjmatthew 	    qle_read_mbox(sc, 2) != 0x5020) {
4553759911cSjmatthew 		/* try releasing the risc processor */
4563759911cSjmatthew 		qle_host_cmd(sc, QLE_HOST_CMD_RELEASE);
4573759911cSjmatthew 	}
4583759911cSjmatthew 
4593759911cSjmatthew 	qle_host_cmd(sc, QLE_HOST_CMD_PAUSE);
4603759911cSjmatthew 	if (qle_softreset(sc) != 0) {
4613759911cSjmatthew 		printf("softreset failed\n");
4623759911cSjmatthew 		goto deintr;
4633759911cSjmatthew 	}
4643759911cSjmatthew 
4653759911cSjmatthew 	if (qle_read_nvram(sc) == 0)
4663759911cSjmatthew 		sc->sc_nvram_valid = 1;
4673759911cSjmatthew 
468c8005c8bSjmatthew #ifdef QLE_NOFIRMWARE
469c8005c8bSjmatthew 	if (qle_verify_firmware(sc, firmware_addr)) {
470c8005c8bSjmatthew 		printf("%s: no firmware loaded\n", DEVNAME(sc));
471c8005c8bSjmatthew 		goto deintr;
472c8005c8bSjmatthew 	}
473c8005c8bSjmatthew #else
474b803df0bSjmatthew 	switch (sc->sc_isp_gen) {
475b803df0bSjmatthew 	case QLE_GEN_ISP24XX:
476c8005c8bSjmatthew 		firmware = isp_2400_risc_code;
477b803df0bSjmatthew 		break;
478b803df0bSjmatthew 	case QLE_GEN_ISP25XX:
479c8005c8bSjmatthew 		firmware = isp_2500_risc_code;
480c8005c8bSjmatthew 		break;
481c8005c8bSjmatthew 	default:
482c8005c8bSjmatthew 		printf("%s: no firmware to load?\n", DEVNAME(sc));
483b803df0bSjmatthew 		goto deintr;
484b803df0bSjmatthew 	}
485c8005c8bSjmatthew 	if (qle_load_firmware_chunks(sc, firmware)) {
486c8005c8bSjmatthew 		printf("%s: firmware load failed\n", DEVNAME(sc));
487c8005c8bSjmatthew 		goto deintr;
488b803df0bSjmatthew 	}
489989131ebSjmatthew #endif
4903759911cSjmatthew 
4913759911cSjmatthew 	/* execute firmware */
4923759911cSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_EXEC_FIRMWARE;
493c8005c8bSjmatthew 	sc->sc_mbox[1] = firmware_addr >> 16;
494c8005c8bSjmatthew 	sc->sc_mbox[2] = firmware_addr & 0xffff;
495c8005c8bSjmatthew #ifdef QLE_NOFIRMWARE
496989131ebSjmatthew 	sc->sc_mbox[3] = 1;
497989131ebSjmatthew #else
4983759911cSjmatthew 	sc->sc_mbox[3] = 0;
499989131ebSjmatthew #endif
500b803df0bSjmatthew 	sc->sc_mbox[4] = 0;
50165fd2507Sjmatthew 	if (qle_mbox(sc, 0x001f)) {
5023759911cSjmatthew 		printf("ISP couldn't exec firmware: %x\n", sc->sc_mbox[0]);
5033759911cSjmatthew 		goto deintr;
5043759911cSjmatthew 	}
5053759911cSjmatthew 
5063759911cSjmatthew 	delay(250000);		/* from isp(4) */
5073759911cSjmatthew 
5083759911cSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_ABOUT_FIRMWARE;
50965fd2507Sjmatthew 	if (qle_mbox(sc, 0x0001)) {
5103759911cSjmatthew 		printf("ISP not talking after firmware exec: %x\n",
5113759911cSjmatthew 		    sc->sc_mbox[0]);
5123759911cSjmatthew 		goto deintr;
5133759911cSjmatthew 	}
5140fb098c3Skettenis 	printf("%s: firmware rev %d.%d.%d, attrs 0x%x\n", DEVNAME(sc),
5150fb098c3Skettenis 	    sc->sc_mbox[1], sc->sc_mbox[2], sc->sc_mbox[3], sc->sc_mbox[6]);
5163759911cSjmatthew 
5173759911cSjmatthew 	sc->sc_maxcmds = 4096;
5183759911cSjmatthew 
5193759911cSjmatthew 	/* reserve queue slots for markers and fabric ops */
5203759911cSjmatthew 	sc->sc_maxcmds -= 2;
5213759911cSjmatthew 
5223759911cSjmatthew 	if (qle_alloc_ccbs(sc)) {
5233759911cSjmatthew 		/* error already printed */
5243759911cSjmatthew 		goto deintr;
5253759911cSjmatthew 	}
5263759911cSjmatthew 	sc->sc_scratch = qle_dmamem_alloc(sc, QLE_SCRATCH_SIZE);
5273759911cSjmatthew 	if (sc->sc_scratch == NULL) {
5283759911cSjmatthew 		printf("%s: unable to allocate scratch\n", DEVNAME(sc));
5293759911cSjmatthew 		goto free_ccbs;
5303759911cSjmatthew 	}
5313759911cSjmatthew 
5323759911cSjmatthew 	/* build init buffer thing */
5333759911cSjmatthew 	icb = (struct qle_init_cb *)QLE_DMA_KVA(sc->sc_scratch);
5343759911cSjmatthew 	memset(icb, 0, sizeof(*icb));
5353759911cSjmatthew 	icb->icb_version = QLE_ICB_VERSION;
5363759911cSjmatthew 	if (sc->sc_nvram_valid) {
5373759911cSjmatthew 		icb->icb_max_frame_len = sc->sc_nvram.frame_payload_size;
5383759911cSjmatthew 		icb->icb_exec_throttle = sc->sc_nvram.execution_throttle;
5393759911cSjmatthew 		icb->icb_hardaddr = sc->sc_nvram.hard_address;
5403759911cSjmatthew 		icb->icb_portname = sc->sc_nvram.port_name;
5413759911cSjmatthew 		icb->icb_nodename = sc->sc_nvram.node_name;
5423759911cSjmatthew 		icb->icb_login_retry = sc->sc_nvram.login_retry;
5433759911cSjmatthew 		icb->icb_login_timeout = sc->sc_nvram.login_timeout;
5443759911cSjmatthew 		icb->icb_fwoptions1 = sc->sc_nvram.fwoptions1;
5453759911cSjmatthew 		icb->icb_fwoptions2 = sc->sc_nvram.fwoptions2;
5463759911cSjmatthew 		icb->icb_fwoptions3 = sc->sc_nvram.fwoptions3;
5473759911cSjmatthew 	} else {
5483759911cSjmatthew 		/* defaults copied from isp(4) */
54961ff084cSdlg 		htolem16(&icb->icb_max_frame_len, 1024);
55061ff084cSdlg 		htolem16(&icb->icb_exec_throttle, 16);
5513759911cSjmatthew 		icb->icb_portname = htobe64(QLE_DEFAULT_PORT_NAME);
5523759911cSjmatthew 		icb->icb_nodename = 0;
5533759911cSjmatthew 		icb->icb_login_retry = 3;
5543759911cSjmatthew 
55561ff084cSdlg 		htolem32(&icb->icb_fwoptions1, QLE_ICB_FW1_FAIRNESS |
55661ff084cSdlg 		    QLE_ICB_FW1_HARD_ADDR | QLE_ICB_FW1_FULL_DUPLEX);
55761ff084cSdlg 		htolem32(&icb->icb_fwoptions2, QLE_ICB_FW2_LOOP_PTP);
55861ff084cSdlg 		htolem32(&icb->icb_fwoptions3, QLE_ICB_FW3_FCP_RSP_24_0 |
5593759911cSjmatthew 		    QLE_ICB_FW3_AUTONEG);
5603759911cSjmatthew 	}
5613759911cSjmatthew 
5623759911cSjmatthew 	icb->icb_exchange_count = 0;
5633759911cSjmatthew 
5643759911cSjmatthew 	icb->icb_req_out = 0;
5653759911cSjmatthew 	icb->icb_resp_in = 0;
5663759911cSjmatthew 	icb->icb_pri_req_out = 0;
56761ff084cSdlg 	htolem16(&icb->icb_req_queue_len, sc->sc_maxcmds);
56861ff084cSdlg 	htolem16(&icb->icb_resp_queue_len, sc->sc_maxcmds);
56961ff084cSdlg 	htolem16(&icb->icb_pri_req_queue_len, 8); /* apparently the minimum */
5709802e30eSdlg 	htolem32(&icb->icb_req_queue_addr_lo,
5719802e30eSdlg 	    QLE_DMA_DVA(sc->sc_requests));
5729802e30eSdlg 	htolem32(&icb->icb_req_queue_addr_hi,
5739802e30eSdlg 	    QLE_DMA_DVA(sc->sc_requests) >> 32);
5749802e30eSdlg 	htolem32(&icb->icb_resp_queue_addr_lo,
5759802e30eSdlg 	    QLE_DMA_DVA(sc->sc_responses));
5769802e30eSdlg 	htolem32(&icb->icb_resp_queue_addr_hi,
5779802e30eSdlg 	    QLE_DMA_DVA(sc->sc_responses) >> 32);
5789802e30eSdlg 	htolem32(&icb->icb_pri_req_queue_addr_lo,
5799802e30eSdlg 	    QLE_DMA_DVA(sc->sc_pri_requests));
5809802e30eSdlg 	htolem32(&icb->icb_pri_req_queue_addr_hi,
5819802e30eSdlg 	    QLE_DMA_DVA(sc->sc_pri_requests) >> 32);
5823759911cSjmatthew 
5839802e30eSdlg 	htolem16(&icb->icb_link_down_nos, 200);
5843759911cSjmatthew 	icb->icb_int_delay = 0;
5853759911cSjmatthew 	icb->icb_login_timeout = 0;
5863759911cSjmatthew 
5873759911cSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_INIT_FIRMWARE;
5883759911cSjmatthew 	sc->sc_mbox[4] = 0;
5893759911cSjmatthew 	sc->sc_mbox[5] = 0;
5903759911cSjmatthew 	qle_mbox_putaddr(sc->sc_mbox, sc->sc_scratch);
5913759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0,
5923759911cSjmatthew 	    sizeof(*icb), BUS_DMASYNC_PREWRITE);
59365fd2507Sjmatthew 	rv = qle_mbox(sc, 0x00fd);
5943759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0,
5953759911cSjmatthew 	    sizeof(*icb), BUS_DMASYNC_POSTWRITE);
5963759911cSjmatthew 
5973759911cSjmatthew 	if (rv != 0) {
5983759911cSjmatthew 		printf("%s: ISP firmware init failed: %x\n", DEVNAME(sc),
5993759911cSjmatthew 		    sc->sc_mbox[0]);
6003759911cSjmatthew 		goto free_scratch;
6013759911cSjmatthew 	}
6023759911cSjmatthew 
6033759911cSjmatthew 	/* enable some more notifications */
6043759911cSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_SET_FIRMWARE_OPTIONS;
6053759911cSjmatthew 	sc->sc_mbox[1] = QLE_FW_OPTION1_ASYNC_LIP_F8 |
6063759911cSjmatthew 	    QLE_FW_OPTION1_ASYNC_LIP_RESET |
6073759911cSjmatthew 	    QLE_FW_OPTION1_ASYNC_LIP_ERROR |
6083759911cSjmatthew 	    QLE_FW_OPTION1_ASYNC_LOGIN_RJT;
6093759911cSjmatthew 	sc->sc_mbox[2] = 0;
6103759911cSjmatthew 	sc->sc_mbox[3] = 0;
61165fd2507Sjmatthew 	if (qle_mbox(sc, 0x000f)) {
6123759911cSjmatthew 		printf("%s: setting firmware options failed: %x\n",
6133759911cSjmatthew 		    DEVNAME(sc), sc->sc_mbox[0]);
6143759911cSjmatthew 		goto free_scratch;
6153759911cSjmatthew 	}
6163759911cSjmatthew 
61779ea9c08Sdlg 	sc->sc_update_taskq = taskq_create(DEVNAME(sc), 1, IPL_BIO, 0);
618e4195480Sdlg 	task_set(&sc->sc_update_task, qle_do_update, sc);
6193fc6fae1Sjmatthew 	timeout_set(&sc->sc_update_timeout, qle_deferred_update, sc);
6203759911cSjmatthew 
6213759911cSjmatthew 	/* wait a bit for link to come up so we can scan and attach devices */
62211e354acSjmatthew 	for (i = 0; i < QLE_WAIT_FOR_LOOP * 1000; i++) {
6233759911cSjmatthew 		u_int16_t isr, info;
6243759911cSjmatthew 
6259382a847Sjmatthew 		if (sc->sc_loop_up) {
6269382a847Sjmatthew 			if (++loop_up == QLE_LOOP_SETTLE)
6273759911cSjmatthew 				break;
6289382a847Sjmatthew 		} else
6299382a847Sjmatthew 			loop_up = 0;
63011e354acSjmatthew 
63111e354acSjmatthew 		delay(1000);
63211e354acSjmatthew 
63311e354acSjmatthew 		if (qle_read_isr(sc, &isr, &info) == 0)
63411e354acSjmatthew 			continue;
63511e354acSjmatthew 
63611e354acSjmatthew 		qle_handle_intr(sc, isr, info);
63711e354acSjmatthew 
6383759911cSjmatthew 	}
6393759911cSjmatthew 
6403759911cSjmatthew 	if (sc->sc_loop_up) {
641e4195480Sdlg 		qle_do_update(sc);
6423759911cSjmatthew 	} else {
643cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: loop still down, giving up\n",
644cc014dc0Sjmatthew 		    DEVNAME(sc));
6453759911cSjmatthew 	}
6463759911cSjmatthew 
647ead808c4Skrw 	saa.saa_adapter = &qle_switch;
648ead808c4Skrw 	saa.saa_adapter_softc = sc;
649ead808c4Skrw 	saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
650ead808c4Skrw 	saa.saa_adapter_buswidth = QLE_MAX_TARGETS;
651ead808c4Skrw 	saa.saa_luns = 8;
652e5eae15dSkrw 	saa.saa_openings = sc->sc_maxcmds;
653e5eae15dSkrw 	saa.saa_pool = &sc->sc_iopool;
654e5eae15dSkrw 	if (sc->sc_nvram_valid) {
655e5eae15dSkrw 		saa.saa_wwpn = betoh64(sc->sc_nvram.port_name);
656e5eae15dSkrw 		saa.saa_wwnn = betoh64(sc->sc_nvram.node_name);
657e5eae15dSkrw 	} else {
658e5eae15dSkrw 		saa.saa_wwpn = QLE_DEFAULT_PORT_NAME;
659e5eae15dSkrw 		saa.saa_wwnn = 0;
660e5eae15dSkrw 	}
661e5eae15dSkrw 	if (saa.saa_wwnn == 0) {
662e5eae15dSkrw 		/*
663e5eae15dSkrw 		 * mask out the port number from the port name to get
664e5eae15dSkrw 		 * the node name.
665e5eae15dSkrw 		 */
666e5eae15dSkrw 		saa.saa_wwnn = saa.saa_wwpn;
667e5eae15dSkrw 		saa.saa_wwnn &= ~(0xfULL << 56);
668e5eae15dSkrw 	}
669e5eae15dSkrw 	saa.saa_quirks = saa.saa_flags = 0;
6703759911cSjmatthew 
6713759911cSjmatthew 	sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev,
6723759911cSjmatthew 	    &saa, scsiprint);
6733759911cSjmatthew 
6743759911cSjmatthew 	return;
6753759911cSjmatthew 
6763759911cSjmatthew free_scratch:
6773759911cSjmatthew 	qle_dmamem_free(sc, sc->sc_scratch);
6783759911cSjmatthew free_ccbs:
6793759911cSjmatthew 	qle_free_ccbs(sc);
6803759911cSjmatthew deintr:
6813759911cSjmatthew 	pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
6823759911cSjmatthew 	sc->sc_ih = NULL;
6833759911cSjmatthew unmap:
6843759911cSjmatthew 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
6853759911cSjmatthew 	sc->sc_ios = 0;
6863759911cSjmatthew }
6873759911cSjmatthew 
6883759911cSjmatthew int
6893759911cSjmatthew qle_detach(struct device *self, int flags)
6903759911cSjmatthew {
6913759911cSjmatthew 	struct qle_softc *sc = (struct qle_softc *)self;
6923759911cSjmatthew 
6933759911cSjmatthew 	if (sc->sc_ih == NULL) {
694*9593dc34Smglocker 		/* we didn't attach properly, so nothing to detach */
6953759911cSjmatthew 		return (0);
6963759911cSjmatthew 	}
6973759911cSjmatthew 
6983759911cSjmatthew 	pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
6993759911cSjmatthew 	sc->sc_ih = NULL;
7003759911cSjmatthew 
7013759911cSjmatthew 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
7023759911cSjmatthew 	sc->sc_ios = 0;
7033759911cSjmatthew 
7043759911cSjmatthew 	return (0);
7053759911cSjmatthew }
7063759911cSjmatthew 
7073759911cSjmatthew int
7083759911cSjmatthew qle_classify_port(struct qle_softc *sc, u_int32_t location,
7093759911cSjmatthew     u_int64_t port_name, u_int64_t node_name, struct qle_fc_port **prev)
7103759911cSjmatthew {
7113759911cSjmatthew 	struct qle_fc_port *port, *locmatch, *wwnmatch;
7123759911cSjmatthew 	locmatch = NULL;
7133759911cSjmatthew 	wwnmatch = NULL;
7143759911cSjmatthew 
7153759911cSjmatthew 	/* make sure we don't try to add a port or location twice */
7163759911cSjmatthew 	TAILQ_FOREACH(port, &sc->sc_ports_new, update) {
7173759911cSjmatthew 		if ((port->port_name == port_name &&
7183759911cSjmatthew 		    port->node_name == node_name) ||
7193759911cSjmatthew 		    port->location == location) {
7203759911cSjmatthew 			*prev = port;
7213759911cSjmatthew 			return (QLE_PORT_DISP_DUP);
7223759911cSjmatthew 		}
7233759911cSjmatthew 	}
7243759911cSjmatthew 
7253759911cSjmatthew 	/* if we're attaching, everything is new */
7263759911cSjmatthew 	if (sc->sc_scsibus == NULL) {
7273759911cSjmatthew 		*prev = NULL;
7283759911cSjmatthew 		return (QLE_PORT_DISP_NEW);
7293759911cSjmatthew 	}
7303759911cSjmatthew 
731580dbe8fSjmatthew 	TAILQ_FOREACH(port, &sc->sc_ports, ports) {
7323759911cSjmatthew 		if (port->location == location)
7333759911cSjmatthew 			locmatch = port;
7343759911cSjmatthew 
7353759911cSjmatthew 		if (port->port_name == port_name &&
7363759911cSjmatthew 		    port->node_name == node_name)
7373759911cSjmatthew 			wwnmatch = port;
7383759911cSjmatthew 	}
7393759911cSjmatthew 
7403759911cSjmatthew 	if (locmatch == NULL && wwnmatch == NULL) {
7413759911cSjmatthew 		*prev = NULL;
7423759911cSjmatthew 		return (QLE_PORT_DISP_NEW);
7433759911cSjmatthew 	} else if (locmatch == wwnmatch) {
7443759911cSjmatthew 		*prev = locmatch;
7453759911cSjmatthew 		return (QLE_PORT_DISP_SAME);
7463759911cSjmatthew 	} else if (wwnmatch != NULL) {
7473759911cSjmatthew 		*prev = wwnmatch;
7483759911cSjmatthew 		return (QLE_PORT_DISP_MOVED);
7493759911cSjmatthew 	} else {
7503759911cSjmatthew 		*prev = locmatch;
7513759911cSjmatthew 		return (QLE_PORT_DISP_CHANGED);
7523759911cSjmatthew 	}
7533759911cSjmatthew }
7543759911cSjmatthew 
7553759911cSjmatthew int
7560d6eccfaSjmatthew qle_get_loop_id(struct qle_softc *sc, int start)
7573759911cSjmatthew {
7583759911cSjmatthew 	int i, last;
7593759911cSjmatthew 
7603759911cSjmatthew 	i = QLE_MIN_HANDLE;
7613759911cSjmatthew 	last = QLE_MAX_HANDLE;
7620d6eccfaSjmatthew 	if (i < start)
7630d6eccfaSjmatthew 		i = start;
7640d6eccfaSjmatthew 
7653759911cSjmatthew 	for (; i <= last; i++) {
7663759911cSjmatthew 		if (sc->sc_targets[i] == NULL)
7673759911cSjmatthew 			return (i);
7683759911cSjmatthew 	}
7693759911cSjmatthew 
7703759911cSjmatthew 	return (-1);
7713759911cSjmatthew }
7723759911cSjmatthew 
7733759911cSjmatthew int
7743759911cSjmatthew qle_get_port_db(struct qle_softc *sc, u_int16_t loopid, struct qle_dmamem *mem)
7753759911cSjmatthew {
7763759911cSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_GET_PORT_DB;
7773759911cSjmatthew 	sc->sc_mbox[1] = loopid;
7783759911cSjmatthew 	qle_mbox_putaddr(sc->sc_mbox, mem);
7793759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0,
7803759911cSjmatthew 	    sizeof(struct qle_get_port_db), BUS_DMASYNC_PREREAD);
78165fd2507Sjmatthew 	if (qle_mbox(sc, 0x00cf)) {
782871bea04Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: get port db for %d failed: %x\n",
7833759911cSjmatthew 		    DEVNAME(sc), loopid, sc->sc_mbox[0]);
7843759911cSjmatthew 		return (1);
7853759911cSjmatthew 	}
7863759911cSjmatthew 
7873759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0,
7883759911cSjmatthew 	    sizeof(struct qle_get_port_db), BUS_DMASYNC_POSTREAD);
7893759911cSjmatthew 	return (0);
7903759911cSjmatthew }
7913759911cSjmatthew 
7923759911cSjmatthew int
793580dbe8fSjmatthew qle_get_port_name_list(struct qle_softc *sc, u_int32_t match)
794580dbe8fSjmatthew {
795580dbe8fSjmatthew 	struct qle_port_name_list *l;
796580dbe8fSjmatthew 	struct qle_fc_port *port;
797580dbe8fSjmatthew 	int i;
798580dbe8fSjmatthew 
799580dbe8fSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_GET_PORT_NAME_LIST;
800580dbe8fSjmatthew 	sc->sc_mbox[1] = 0;
801580dbe8fSjmatthew 	sc->sc_mbox[8] = QLE_DMA_LEN(sc->sc_scratch);
802580dbe8fSjmatthew 	sc->sc_mbox[9] = 0;
803580dbe8fSjmatthew 	qle_mbox_putaddr(sc->sc_mbox, sc->sc_scratch);
804580dbe8fSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0,
805580dbe8fSjmatthew 	    QLE_DMA_LEN(sc->sc_scratch), BUS_DMASYNC_PREREAD);
806580dbe8fSjmatthew 	if (qle_mbox(sc, 0x03cf)) {
807580dbe8fSjmatthew 		DPRINTF(QLE_D_PORT, "%s: get port name list failed: %x\n",
808580dbe8fSjmatthew 		    DEVNAME(sc), sc->sc_mbox[0]);
809580dbe8fSjmatthew 		return (1);
810580dbe8fSjmatthew 	}
811580dbe8fSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0,
812580dbe8fSjmatthew 	    sc->sc_mbox[1], BUS_DMASYNC_POSTREAD);
813580dbe8fSjmatthew 
814580dbe8fSjmatthew 	i = 0;
815580dbe8fSjmatthew 	l = QLE_DMA_KVA(sc->sc_scratch);
816580dbe8fSjmatthew 	mtx_enter(&sc->sc_port_mtx);
817580dbe8fSjmatthew 	while (i * sizeof(*l) < sc->sc_mbox[1]) {
818580dbe8fSjmatthew 		u_int16_t loopid;
819580dbe8fSjmatthew 		u_int32_t loc;
820580dbe8fSjmatthew 
821580dbe8fSjmatthew 		loopid = lemtoh16(&l[i].loopid) & 0xfff;
822580dbe8fSjmatthew 		/* skip special ports */
823580dbe8fSjmatthew 		switch (loopid) {
824580dbe8fSjmatthew 		case QLE_F_PORT_HANDLE:
825580dbe8fSjmatthew 		case QLE_SNS_HANDLE:
826580dbe8fSjmatthew 		case QLE_FABRIC_CTRL_HANDLE:
827580dbe8fSjmatthew 		case QLE_IP_BCAST_HANDLE:
828580dbe8fSjmatthew 			loc = 0;
829580dbe8fSjmatthew 			break;
830580dbe8fSjmatthew 		default:
831580dbe8fSjmatthew 			if (loopid <= sc->sc_loop_max_id) {
832580dbe8fSjmatthew 				loc = QLE_LOCATION_LOOP_ID(loopid);
833580dbe8fSjmatthew 			} else {
834580dbe8fSjmatthew 				/*
835580dbe8fSjmatthew 				 * we don't have the port id here, so just
836580dbe8fSjmatthew 				 * indicate it's a fabric port.
837580dbe8fSjmatthew 				 */
838580dbe8fSjmatthew 				loc = QLE_LOCATION_FABRIC;
839580dbe8fSjmatthew 			}
840580dbe8fSjmatthew 			break;
841580dbe8fSjmatthew 		}
842580dbe8fSjmatthew 
843580dbe8fSjmatthew 		if (match & loc) {
844580dbe8fSjmatthew 			port = malloc(sizeof(*port), M_DEVBUF, M_ZERO |
845580dbe8fSjmatthew 			    M_NOWAIT);
846580dbe8fSjmatthew 			if (port == NULL) {
847580dbe8fSjmatthew 				printf("%s: failed to allocate port struct\n",
848580dbe8fSjmatthew 				    DEVNAME(sc));
849580dbe8fSjmatthew 				break;
850580dbe8fSjmatthew 			}
851580dbe8fSjmatthew 			port->location = loc;
852580dbe8fSjmatthew 			port->loopid = loopid;
853580dbe8fSjmatthew 			port->port_name = letoh64(l[i].port_name);
854580dbe8fSjmatthew 			DPRINTF(QLE_D_PORT, "%s: loop id %d, port name %llx\n",
855580dbe8fSjmatthew 			    DEVNAME(sc), port->loopid, port->port_name);
856580dbe8fSjmatthew 			TAILQ_INSERT_TAIL(&sc->sc_ports_found, port, update);
857580dbe8fSjmatthew 		}
858580dbe8fSjmatthew 		i++;
859580dbe8fSjmatthew 	}
860580dbe8fSjmatthew 	mtx_leave(&sc->sc_port_mtx);
861580dbe8fSjmatthew 
862580dbe8fSjmatthew 	return (0);
863580dbe8fSjmatthew }
864580dbe8fSjmatthew 
865580dbe8fSjmatthew int
866580dbe8fSjmatthew qle_add_loop_port(struct qle_softc *sc, struct qle_fc_port *port)
8673759911cSjmatthew {
8683759911cSjmatthew 	struct qle_get_port_db *pdb;
869580dbe8fSjmatthew 	struct qle_fc_port *pport;
8703759911cSjmatthew 	int disp;
8713759911cSjmatthew 
872580dbe8fSjmatthew 	if (qle_get_port_db(sc, port->loopid, sc->sc_scratch) != 0) {
8733759911cSjmatthew 		return (1);
8743759911cSjmatthew 	}
8753759911cSjmatthew 	pdb = QLE_DMA_KVA(sc->sc_scratch);
8763759911cSjmatthew 
87761ff084cSdlg 	if (lemtoh16(&pdb->prli_svc_word3) & QLE_SVC3_TARGET_ROLE)
8783759911cSjmatthew 		port->flags |= QLE_PORT_FLAG_IS_TARGET;
8793759911cSjmatthew 
8803759911cSjmatthew 	port->port_name = betoh64(pdb->port_name);
8813759911cSjmatthew 	port->node_name = betoh64(pdb->node_name);
8823759911cSjmatthew 	port->portid = (pdb->port_id[0] << 16) | (pdb->port_id[1] << 8) |
8833759911cSjmatthew 	    pdb->port_id[2];
8843759911cSjmatthew 
8853759911cSjmatthew 	mtx_enter(&sc->sc_port_mtx);
8863759911cSjmatthew 	disp = qle_classify_port(sc, port->location, port->port_name,
8873759911cSjmatthew 	    port->node_name, &pport);
8883759911cSjmatthew 	switch (disp) {
8893759911cSjmatthew 	case QLE_PORT_DISP_CHANGED:
8903759911cSjmatthew 	case QLE_PORT_DISP_MOVED:
8913759911cSjmatthew 	case QLE_PORT_DISP_NEW:
8923759911cSjmatthew 		TAILQ_INSERT_TAIL(&sc->sc_ports_new, port, update);
893580dbe8fSjmatthew 		sc->sc_targets[port->loopid] = port;
8943759911cSjmatthew 		break;
8953759911cSjmatthew 	case QLE_PORT_DISP_DUP:
896c9d76b5bSderaadt 		free(port, M_DEVBUF, sizeof *port);
8973759911cSjmatthew 		break;
8983759911cSjmatthew 	case QLE_PORT_DISP_SAME:
8993759911cSjmatthew 		TAILQ_REMOVE(&sc->sc_ports_gone, pport, update);
900c9d76b5bSderaadt 		free(port, M_DEVBUF, sizeof *port);
9013759911cSjmatthew 		break;
9023759911cSjmatthew 	}
9033759911cSjmatthew 	mtx_leave(&sc->sc_port_mtx);
9043759911cSjmatthew 
9053759911cSjmatthew 	switch (disp) {
9063759911cSjmatthew 	case QLE_PORT_DISP_CHANGED:
9073759911cSjmatthew 	case QLE_PORT_DISP_MOVED:
9083759911cSjmatthew 	case QLE_PORT_DISP_NEW:
909cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: %s %d; name %llx\n",
9103759911cSjmatthew 		    DEVNAME(sc), ISSET(port->flags, QLE_PORT_FLAG_IS_TARGET) ?
911580dbe8fSjmatthew 		    "target" : "non-target", port->loopid,
912580dbe8fSjmatthew 		    betoh64(pdb->port_name));
9133759911cSjmatthew 		break;
9143759911cSjmatthew 	default:
9153759911cSjmatthew 		break;
9163759911cSjmatthew 	}
9173759911cSjmatthew 	return (0);
9183759911cSjmatthew }
9193759911cSjmatthew 
9203759911cSjmatthew int
9213759911cSjmatthew qle_add_fabric_port(struct qle_softc *sc, struct qle_fc_port *port)
9223759911cSjmatthew {
9233759911cSjmatthew 	struct qle_get_port_db *pdb;
9243759911cSjmatthew 
9253759911cSjmatthew 	if (qle_get_port_db(sc, port->loopid, sc->sc_scratch) != 0) {
926c9d76b5bSderaadt 		free(port, M_DEVBUF, sizeof *port);
9273759911cSjmatthew 		return (1);
9283759911cSjmatthew 	}
9293759911cSjmatthew 	pdb = QLE_DMA_KVA(sc->sc_scratch);
9303759911cSjmatthew 
93161ff084cSdlg 	if (lemtoh16(&pdb->prli_svc_word3) & QLE_SVC3_TARGET_ROLE)
9323759911cSjmatthew 		port->flags |= QLE_PORT_FLAG_IS_TARGET;
9333759911cSjmatthew 
934580dbe8fSjmatthew 	/*
935580dbe8fSjmatthew 	 * if we only know about this port because qle_get_port_name_list
936580dbe8fSjmatthew 	 * returned it, we don't have its port id or node name, so fill
937580dbe8fSjmatthew 	 * those in and update its location.
938580dbe8fSjmatthew 	 */
939580dbe8fSjmatthew 	if (port->location == QLE_LOCATION_FABRIC) {
940580dbe8fSjmatthew 		port->node_name = betoh64(pdb->node_name);
9410d6eccfaSjmatthew 		port->port_name = betoh64(pdb->port_name);
942580dbe8fSjmatthew 		port->portid = (pdb->port_id[0] << 16) |
943580dbe8fSjmatthew 		    (pdb->port_id[1] << 8) | pdb->port_id[2];
944580dbe8fSjmatthew 		port->location = QLE_LOCATION_PORT_ID(port->portid);
945580dbe8fSjmatthew 	}
9463759911cSjmatthew 
9473759911cSjmatthew 	mtx_enter(&sc->sc_port_mtx);
9483759911cSjmatthew 	TAILQ_INSERT_TAIL(&sc->sc_ports_new, port, update);
9493759911cSjmatthew 	sc->sc_targets[port->loopid] = port;
9503759911cSjmatthew 	mtx_leave(&sc->sc_port_mtx);
9513759911cSjmatthew 
952cc014dc0Sjmatthew 	DPRINTF(QLE_D_PORT, "%s: %s %d; name %llx\n",
9533759911cSjmatthew 	    DEVNAME(sc), ISSET(port->flags, QLE_PORT_FLAG_IS_TARGET) ?
9543759911cSjmatthew 	    "target" : "non-target", port->loopid, port->port_name);
9553759911cSjmatthew 	return (0);
9563759911cSjmatthew }
9573759911cSjmatthew 
958b9154820Sjmatthew int
9590d6eccfaSjmatthew qle_add_logged_in_port(struct qle_softc *sc, u_int16_t loopid,
960b9154820Sjmatthew     u_int32_t portid)
961b9154820Sjmatthew {
962b9154820Sjmatthew 	struct qle_fc_port *port;
9630d6eccfaSjmatthew 	struct qle_get_port_db *pdb;
9640d6eccfaSjmatthew 	u_int64_t node_name, port_name;
9650d6eccfaSjmatthew 	int flags, ret;
9660d6eccfaSjmatthew 
9670d6eccfaSjmatthew 	ret = qle_get_port_db(sc, loopid, sc->sc_scratch);
9680d6eccfaSjmatthew 	mtx_enter(&sc->sc_port_mtx);
9690d6eccfaSjmatthew 	if (ret != 0) {
9700d6eccfaSjmatthew 		/* put in a fake port to prevent use of this loop id */
9710d6eccfaSjmatthew 		printf("%s: loop id %d used, but can't see what's using it\n",
9720d6eccfaSjmatthew 		    DEVNAME(sc), loopid);
9730d6eccfaSjmatthew 		node_name = 0;
9740d6eccfaSjmatthew 		port_name = 0;
9750d6eccfaSjmatthew 		flags = 0;
9760d6eccfaSjmatthew 	} else {
9770d6eccfaSjmatthew 		pdb = QLE_DMA_KVA(sc->sc_scratch);
9780d6eccfaSjmatthew 		node_name = betoh64(pdb->node_name);
9790d6eccfaSjmatthew 		port_name = betoh64(pdb->port_name);
9800d6eccfaSjmatthew 		flags = 0;
9810d6eccfaSjmatthew 		if (lemtoh16(&pdb->prli_svc_word3) & QLE_SVC3_TARGET_ROLE)
9820d6eccfaSjmatthew 			flags |= QLE_PORT_FLAG_IS_TARGET;
9830d6eccfaSjmatthew 
9840d6eccfaSjmatthew 		/* see if we've already found this port */
9850d6eccfaSjmatthew 		TAILQ_FOREACH(port, &sc->sc_ports_found, update) {
9860d6eccfaSjmatthew 			if ((port->node_name == node_name) &&
9870d6eccfaSjmatthew 			    (port->port_name == port_name) &&
9880d6eccfaSjmatthew 			    (port->portid == portid)) {
9890d6eccfaSjmatthew 				mtx_leave(&sc->sc_port_mtx);
9900d6eccfaSjmatthew 				DPRINTF(QLE_D_PORT, "%s: already found port "
9910d6eccfaSjmatthew 				    "%06x\n", DEVNAME(sc), portid);
9920d6eccfaSjmatthew 				return (0);
9930d6eccfaSjmatthew 			}
9940d6eccfaSjmatthew 		}
9950d6eccfaSjmatthew 	}
996b9154820Sjmatthew 
997b9154820Sjmatthew 	port = malloc(sizeof(*port), M_DEVBUF, M_ZERO | M_NOWAIT);
998b9154820Sjmatthew 	if (port == NULL) {
9990d6eccfaSjmatthew 		mtx_leave(&sc->sc_port_mtx);
1000b9154820Sjmatthew 		printf("%s: failed to allocate a port structure\n",
1001b9154820Sjmatthew 		    DEVNAME(sc));
1002b9154820Sjmatthew 		return (1);
1003b9154820Sjmatthew 	}
1004b9154820Sjmatthew 	port->location = QLE_LOCATION_PORT_ID(portid);
10050d6eccfaSjmatthew 	port->port_name = port_name;
10060d6eccfaSjmatthew 	port->node_name = node_name;
1007b9154820Sjmatthew 	port->loopid = loopid;
1008b9154820Sjmatthew 	port->portid = portid;
10090d6eccfaSjmatthew 	port->flags = flags;
1010b9154820Sjmatthew 
1011b9154820Sjmatthew 	TAILQ_INSERT_TAIL(&sc->sc_ports, port, ports);
1012b9154820Sjmatthew 	sc->sc_targets[port->loopid] = port;
1013b9154820Sjmatthew 	mtx_leave(&sc->sc_port_mtx);
1014b9154820Sjmatthew 
10150d6eccfaSjmatthew 	DPRINTF(QLE_D_PORT, "%s: added logged in port %06x at %d\n",
1016b9154820Sjmatthew 	    DEVNAME(sc), portid, loopid);
1017b9154820Sjmatthew 	return (0);
1018b9154820Sjmatthew }
1019b9154820Sjmatthew 
10203759911cSjmatthew struct qle_ccb *
1021ce28b8eaSdlg qle_handle_resp(struct qle_softc *sc, u_int32_t id)
10223759911cSjmatthew {
10233759911cSjmatthew 	struct qle_ccb *ccb;
10243759911cSjmatthew 	struct qle_iocb_status *status;
10253759911cSjmatthew 	struct qle_iocb_req6 *req;
10263759911cSjmatthew 	struct scsi_xfer *xs;
10273759911cSjmatthew 	u_int32_t handle;
10283759911cSjmatthew 	u_int16_t completion;
10293759911cSjmatthew 	u_int8_t *entry;
10303759911cSjmatthew 	u_int8_t *data;
10313759911cSjmatthew 
10323759911cSjmatthew 	ccb = NULL;
10333759911cSjmatthew 	entry = QLE_DMA_KVA(sc->sc_responses) + (id * QLE_QUEUE_ENTRY_SIZE);
10343759911cSjmatthew 
10353759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat,
10363759911cSjmatthew 	    QLE_DMA_MAP(sc->sc_responses), id * QLE_QUEUE_ENTRY_SIZE,
10373759911cSjmatthew 	    QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTREAD);
10383759911cSjmatthew 
1039cc014dc0Sjmatthew 	qle_dump_iocb(sc, entry);
10403759911cSjmatthew 	switch(entry[0]) {
10413759911cSjmatthew 	case QLE_IOCB_STATUS:
10423759911cSjmatthew 		status = (struct qle_iocb_status *)entry;
10433759911cSjmatthew 		handle = status->handle;
10443759911cSjmatthew 		if (handle > sc->sc_maxcmds) {
10453759911cSjmatthew 			panic("bad completed command handle: %d (> %d)",
10463759911cSjmatthew 			    handle, sc->sc_maxcmds);
10473759911cSjmatthew 		}
10483759911cSjmatthew 
10493759911cSjmatthew 		ccb = &sc->sc_ccbs[handle];
10503759911cSjmatthew 		xs = ccb->ccb_xs;
10513759911cSjmatthew 		if (xs == NULL) {
1052cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: got status for inactive ccb %d\n",
10533759911cSjmatthew 			    DEVNAME(sc), handle);
10543759911cSjmatthew 			ccb = NULL;
10553759911cSjmatthew 			break;
10563759911cSjmatthew 		}
10573759911cSjmatthew 		if (xs->io != ccb) {
10583759911cSjmatthew 			panic("completed command handle doesn't match xs "
10593759911cSjmatthew 			    "(handle %d, ccb %p, xs->io %p)", handle, ccb,
10603759911cSjmatthew 			    xs->io);
10613759911cSjmatthew 		}
10623759911cSjmatthew 
10633759911cSjmatthew 		if (xs->datalen > 0) {
10643759911cSjmatthew 			if (ccb->ccb_dmamap->dm_nsegs >
10653759911cSjmatthew 			    QLE_IOCB_SEGS_PER_CMD) {
10663759911cSjmatthew 				bus_dmamap_sync(sc->sc_dmat,
10673759911cSjmatthew 				    QLE_DMA_MAP(sc->sc_segments),
10683759911cSjmatthew 				    ccb->ccb_seg_offset,
10693759911cSjmatthew 				    sizeof(*ccb->ccb_segs) *
107020c4379eSjmatthew 				    ccb->ccb_dmamap->dm_nsegs + 1,
10713759911cSjmatthew 				    BUS_DMASYNC_POSTWRITE);
10723759911cSjmatthew 			}
10733759911cSjmatthew 
10743759911cSjmatthew 			bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0,
10753759911cSjmatthew 			    ccb->ccb_dmamap->dm_mapsize,
10763759911cSjmatthew 			    (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD :
10773759911cSjmatthew 			    BUS_DMASYNC_POSTWRITE);
10783759911cSjmatthew 			bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap);
10793759911cSjmatthew 		}
10803759911cSjmatthew 
108120c4379eSjmatthew 		xs->status = lemtoh16(&status->scsi_status) & 0xff;
108220c4379eSjmatthew 		xs->resid = 0;
108361ff084cSdlg 		completion = lemtoh16(&status->completion);
10843759911cSjmatthew 		switch (completion) {
10853759911cSjmatthew 		case QLE_IOCB_STATUS_DATA_UNDERRUN:
108661ff084cSdlg 			xs->resid = lemtoh32(&status->resid);
108772c1becbSjmatthew 			/* FALLTHROUGH */
108820c4379eSjmatthew 		case QLE_IOCB_STATUS_DATA_OVERRUN:
108920c4379eSjmatthew 		case QLE_IOCB_STATUS_COMPLETE:
109061ff084cSdlg 			if (lemtoh16(&status->scsi_status) &
10913759911cSjmatthew 			    QLE_SCSI_STATUS_SENSE_VALID) {
10923759911cSjmatthew 				u_int32_t *pp;
10933759911cSjmatthew 				int sr;
10943759911cSjmatthew 				data = status->data +
109561ff084cSdlg 				    lemtoh32(&status->fcp_rsp_len);
1096ecca1b20Sjmatthew 				sr = MIN(lemtoh32(&status->fcp_sense_len),
1097ecca1b20Sjmatthew 				    sizeof(xs->sense));
1098ecca1b20Sjmatthew 				memcpy(&xs->sense, data, sr);
10993759911cSjmatthew 				xs->error = XS_SENSE;
11003759911cSjmatthew 				pp = (u_int32_t *)&xs->sense;
11013759911cSjmatthew 				for (sr = 0; sr < sizeof(xs->sense)/4; sr++) {
11023759911cSjmatthew 					pp[sr] = swap32(pp[sr]);
11033759911cSjmatthew 				}
11043759911cSjmatthew 			} else {
11053759911cSjmatthew 				xs->error = XS_NOERROR;
11063759911cSjmatthew 			}
11073759911cSjmatthew 			break;
11083759911cSjmatthew 
11093759911cSjmatthew 		case QLE_IOCB_STATUS_DMA_ERROR:
1110cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: dma error\n", DEVNAME(sc));
11113759911cSjmatthew 			/* set resid apparently? */
11123759911cSjmatthew 			break;
11133759911cSjmatthew 
11143759911cSjmatthew 		case QLE_IOCB_STATUS_RESET:
1115cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: reset destroyed command\n",
1116cc014dc0Sjmatthew 			    DEVNAME(sc));
11173759911cSjmatthew 			sc->sc_marker_required = 1;
11183759911cSjmatthew 			xs->error = XS_RESET;
11193759911cSjmatthew 			break;
11203759911cSjmatthew 
11213759911cSjmatthew 		case QLE_IOCB_STATUS_ABORTED:
1122cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: aborted\n", DEVNAME(sc));
11233759911cSjmatthew 			sc->sc_marker_required = 1;
11243759911cSjmatthew 			xs->error = XS_DRIVER_STUFFUP;
11253759911cSjmatthew 			break;
11263759911cSjmatthew 
11273759911cSjmatthew 		case QLE_IOCB_STATUS_TIMEOUT:
1128cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: command timed out\n",
1129cc014dc0Sjmatthew 			    DEVNAME(sc));
11303759911cSjmatthew 			xs->error = XS_TIMEOUT;
11313759911cSjmatthew 			break;
11323759911cSjmatthew 
11333759911cSjmatthew 		case QLE_IOCB_STATUS_QUEUE_FULL:
1134cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: queue full\n", DEVNAME(sc));
11353759911cSjmatthew 			xs->error = XS_BUSY;
11363759911cSjmatthew 			break;
11373759911cSjmatthew 
11383759911cSjmatthew 		case QLE_IOCB_STATUS_PORT_UNAVAIL:
11393759911cSjmatthew 		case QLE_IOCB_STATUS_PORT_LOGGED_OUT:
11403759911cSjmatthew 		case QLE_IOCB_STATUS_PORT_CHANGED:
1141cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: dev gone\n", DEVNAME(sc));
11423759911cSjmatthew 			xs->error = XS_SELTIMEOUT;
1143580dbe8fSjmatthew 			/* mark port as needing relogin? */
11443759911cSjmatthew 			break;
11453759911cSjmatthew 
11463759911cSjmatthew 		default:
1147cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: unexpected completion status "
1148cc014dc0Sjmatthew 			    "%x\n", DEVNAME(sc), status->completion);
11493759911cSjmatthew 			xs->error = XS_DRIVER_STUFFUP;
11503759911cSjmatthew 			break;
11513759911cSjmatthew 		}
11523759911cSjmatthew 		break;
11533759911cSjmatthew 
11543759911cSjmatthew 	case QLE_IOCB_STATUS_CONT:
1155cc014dc0Sjmatthew 		DPRINTF(QLE_D_IO, "%s: ignoring status continuation iocb\n",
11563759911cSjmatthew 		    DEVNAME(sc));
11573759911cSjmatthew 		break;
11583759911cSjmatthew 
11593759911cSjmatthew 	case QLE_IOCB_PLOGX:
11603759911cSjmatthew 	case QLE_IOCB_CT_PASSTHROUGH:
11613759911cSjmatthew 		if (sc->sc_fabric_pending) {
1162cc014dc0Sjmatthew 			qle_dump_iocb(sc, entry);
1163e5d3c726Sjmatthew 			memcpy(sc->sc_fabric_response, entry,
1164e5d3c726Sjmatthew 			    QLE_QUEUE_ENTRY_SIZE);
11653759911cSjmatthew 			sc->sc_fabric_pending = 2;
11663759911cSjmatthew 			wakeup(sc->sc_scratch);
11673759911cSjmatthew 		} else {
1168cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: unexpected fabric response %x\n",
11693759911cSjmatthew 			    DEVNAME(sc), entry[0]);
11703759911cSjmatthew 		}
11713759911cSjmatthew 		break;
11723759911cSjmatthew 
11733759911cSjmatthew 	case QLE_IOCB_MARKER:
11743759911cSjmatthew 		break;
11753759911cSjmatthew 
11763759911cSjmatthew 	case QLE_IOCB_CMD_TYPE_6:
11773759911cSjmatthew 	case QLE_IOCB_CMD_TYPE_7:
1178cc014dc0Sjmatthew 		DPRINTF(QLE_D_IO, "%s: request bounced back\n", DEVNAME(sc));
11793759911cSjmatthew 		req = (struct qle_iocb_req6 *)entry;
11803759911cSjmatthew 		handle = req->req_handle;
11813759911cSjmatthew 		if (handle > sc->sc_maxcmds) {
11823759911cSjmatthew 			panic("bad bounced command handle: %d (> %d)",
11833759911cSjmatthew 			    handle, sc->sc_maxcmds);
11843759911cSjmatthew 		}
11853759911cSjmatthew 
11863759911cSjmatthew 		ccb = &sc->sc_ccbs[handle];
11873759911cSjmatthew 		xs = ccb->ccb_xs;
11883759911cSjmatthew 		xs->error = XS_DRIVER_STUFFUP;
11893759911cSjmatthew 		break;
11903759911cSjmatthew 	default:
1191cc014dc0Sjmatthew 		DPRINTF(QLE_D_IO, "%s: unexpected response entry type %x\n",
11923759911cSjmatthew 		    DEVNAME(sc), entry[0]);
11933759911cSjmatthew 		break;
11943759911cSjmatthew 	}
11953759911cSjmatthew 
11963759911cSjmatthew 	return (ccb);
11973759911cSjmatthew }
11983759911cSjmatthew 
11993759911cSjmatthew void
12003759911cSjmatthew qle_handle_intr(struct qle_softc *sc, u_int16_t isr, u_int16_t info)
12013759911cSjmatthew {
12023759911cSjmatthew 	int i;
1203ce28b8eaSdlg 	u_int32_t rspin;
12043759911cSjmatthew 	struct qle_ccb *ccb;
12053759911cSjmatthew 
12063759911cSjmatthew 	switch (isr) {
12073759911cSjmatthew 	case QLE_INT_TYPE_ASYNC:
12083759911cSjmatthew 		qle_async(sc, info);
12093759911cSjmatthew 		break;
12103759911cSjmatthew 
12113759911cSjmatthew 	case QLE_INT_TYPE_IO:
12123759911cSjmatthew 		rspin = qle_read(sc, QLE_RESP_IN);
1213fb48a412Sdlg 		if (rspin == sc->sc_last_resp_id)
1214fb48a412Sdlg 			break;
1215fb48a412Sdlg 
1216fb48a412Sdlg 		do {
12173759911cSjmatthew 			ccb = qle_handle_resp(sc, sc->sc_last_resp_id);
12183759911cSjmatthew 			if (ccb)
12193759911cSjmatthew 				scsi_done(ccb->ccb_xs);
12203759911cSjmatthew 
12213759911cSjmatthew 			sc->sc_last_resp_id++;
1222fb48a412Sdlg 			sc->sc_last_resp_id %= sc->sc_maxcmds;
1223fb48a412Sdlg 		} while (sc->sc_last_resp_id != rspin);
12243759911cSjmatthew 
12253759911cSjmatthew 		qle_write(sc, QLE_RESP_OUT, sc->sc_last_resp_id);
12263759911cSjmatthew 		break;
12273759911cSjmatthew 
12283759911cSjmatthew 	case QLE_INT_TYPE_MBOX:
12293759911cSjmatthew 		mtx_enter(&sc->sc_mbox_mtx);
12303759911cSjmatthew 		if (sc->sc_mbox_pending) {
123165fd2507Sjmatthew 			for (i = 0; i < nitems(sc->sc_mbox); i++) {
12323759911cSjmatthew 				sc->sc_mbox[i] = qle_read_mbox(sc, i);
12333759911cSjmatthew 			}
12343759911cSjmatthew 			sc->sc_mbox_pending = 2;
12353759911cSjmatthew 			wakeup(sc->sc_mbox);
12363759911cSjmatthew 			mtx_leave(&sc->sc_mbox_mtx);
12373759911cSjmatthew 		} else {
12383759911cSjmatthew 			mtx_leave(&sc->sc_mbox_mtx);
1239cc014dc0Sjmatthew 			DPRINTF(QLE_D_INTR, "%s: unexpected mbox interrupt: "
1240cc014dc0Sjmatthew 			    "%x\n", DEVNAME(sc), info);
12413759911cSjmatthew 		}
12423759911cSjmatthew 		break;
12433759911cSjmatthew 
12443759911cSjmatthew 	default:
12453759911cSjmatthew 		break;
12463759911cSjmatthew 	}
12473759911cSjmatthew 
12483759911cSjmatthew 	qle_clear_isr(sc, isr);
12493759911cSjmatthew }
12503759911cSjmatthew 
12513759911cSjmatthew int
12523759911cSjmatthew qle_intr(void *xsc)
12533759911cSjmatthew {
12543759911cSjmatthew 	struct qle_softc *sc = xsc;
12553759911cSjmatthew 	u_int16_t isr;
12563759911cSjmatthew 	u_int16_t info;
12573759911cSjmatthew 
12583759911cSjmatthew 	if (qle_read_isr(sc, &isr, &info) == 0)
12593759911cSjmatthew 		return (0);
12603759911cSjmatthew 
12613759911cSjmatthew 	qle_handle_intr(sc, isr, info);
12623759911cSjmatthew 	return (1);
12633759911cSjmatthew }
12643759911cSjmatthew 
12653759911cSjmatthew int
12663759911cSjmatthew qle_scsi_probe(struct scsi_link *link)
12673759911cSjmatthew {
12680b29cb40Skrw 	struct qle_softc *sc = link->bus->sb_adapter_softc;
12693759911cSjmatthew 	int rv = 0;
12703759911cSjmatthew 
12713759911cSjmatthew 	mtx_enter(&sc->sc_port_mtx);
12723759911cSjmatthew 	if (sc->sc_targets[link->target] == NULL)
12733759911cSjmatthew 		rv = ENXIO;
12743759911cSjmatthew 	else if (!ISSET(sc->sc_targets[link->target]->flags,
12753759911cSjmatthew 	    QLE_PORT_FLAG_IS_TARGET))
12763759911cSjmatthew 		rv = ENXIO;
12773759911cSjmatthew 	mtx_leave(&sc->sc_port_mtx);
12783759911cSjmatthew 
12793759911cSjmatthew 	return (rv);
12803759911cSjmatthew }
12813759911cSjmatthew 
12823759911cSjmatthew void
12833759911cSjmatthew qle_scsi_cmd(struct scsi_xfer *xs)
12843759911cSjmatthew {
12853759911cSjmatthew 	struct scsi_link	*link = xs->sc_link;
12860b29cb40Skrw 	struct qle_softc	*sc = link->bus->sb_adapter_softc;
12873759911cSjmatthew 	struct qle_ccb		*ccb;
12883759911cSjmatthew 	void			*iocb;
12893759911cSjmatthew 	struct qle_ccb_list	list;
12903759911cSjmatthew 	u_int16_t		req;
12913759911cSjmatthew 	u_int32_t		portid;
1292a1184b53Sjmatthew 	int			offset, error, done;
12933759911cSjmatthew 	bus_dmamap_t		dmap;
12943759911cSjmatthew 
12953759911cSjmatthew 	if (xs->cmdlen > 16) {
1296cc014dc0Sjmatthew 		DPRINTF(QLE_D_IO, "%s: cmd too big (%d)\n", DEVNAME(sc),
1297cc014dc0Sjmatthew 		    xs->cmdlen);
12983759911cSjmatthew 		memset(&xs->sense, 0, sizeof(xs->sense));
12993759911cSjmatthew 		xs->sense.error_code = SSD_ERRCODE_VALID | SSD_ERRCODE_CURRENT;
13003759911cSjmatthew 		xs->sense.flags = SKEY_ILLEGAL_REQUEST;
13013759911cSjmatthew 		xs->sense.add_sense_code = 0x20;
13023759911cSjmatthew 		xs->error = XS_SENSE;
13033759911cSjmatthew 		scsi_done(xs);
13043759911cSjmatthew 		return;
13053759911cSjmatthew 	}
13063759911cSjmatthew 
13073759911cSjmatthew 	portid = 0xffffffff;
13083759911cSjmatthew 	mtx_enter(&sc->sc_port_mtx);
13093759911cSjmatthew 	if (sc->sc_targets[xs->sc_link->target] != NULL) {
13103759911cSjmatthew 		portid = sc->sc_targets[xs->sc_link->target]->portid;
13113759911cSjmatthew 	}
13123759911cSjmatthew 	mtx_leave(&sc->sc_port_mtx);
13133759911cSjmatthew 	if (portid == 0xffffffff) {
13143759911cSjmatthew 		xs->error = XS_DRIVER_STUFFUP;
13153759911cSjmatthew 		scsi_done(xs);
13163759911cSjmatthew 		return;
13173759911cSjmatthew 	}
13183759911cSjmatthew 
13193759911cSjmatthew 	ccb = xs->io;
13203759911cSjmatthew 	dmap = ccb->ccb_dmamap;
13213759911cSjmatthew 	if (xs->datalen > 0) {
13223759911cSjmatthew 		error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data,
13233759911cSjmatthew 		    xs->datalen, NULL, (xs->flags & SCSI_NOSLEEP) ?
13243759911cSjmatthew 		    BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
13253759911cSjmatthew 		if (error) {
13263759911cSjmatthew 			xs->error = XS_DRIVER_STUFFUP;
13273759911cSjmatthew 			scsi_done(xs);
13283759911cSjmatthew 			return;
13293759911cSjmatthew 		}
13303759911cSjmatthew 
13313759911cSjmatthew 		bus_dmamap_sync(sc->sc_dmat, dmap, 0,
13323759911cSjmatthew 		    dmap->dm_mapsize,
13333759911cSjmatthew 		    (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
13343759911cSjmatthew 		    BUS_DMASYNC_PREWRITE);
13353759911cSjmatthew 	}
13363759911cSjmatthew 
13373759911cSjmatthew 	mtx_enter(&sc->sc_queue_mtx);
13383759911cSjmatthew 
13393759911cSjmatthew 	/* put in a sync marker if required */
13403759911cSjmatthew 	if (sc->sc_marker_required) {
13413759911cSjmatthew 		req = sc->sc_next_req_id++;
13423759911cSjmatthew 		if (sc->sc_next_req_id == sc->sc_maxcmds)
13433759911cSjmatthew 			sc->sc_next_req_id = 0;
13443759911cSjmatthew 
1345cc014dc0Sjmatthew 		DPRINTF(QLE_D_IO, "%s: writing marker at request %d\n",
1346cc014dc0Sjmatthew 		    DEVNAME(sc), req);
13473759911cSjmatthew 		offset = (req * QLE_QUEUE_ENTRY_SIZE);
13483759911cSjmatthew 		iocb = QLE_DMA_KVA(sc->sc_requests) + offset;
13493759911cSjmatthew 		bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests),
13503759911cSjmatthew 		    offset, QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE);
13513759911cSjmatthew 		qle_put_marker(sc, iocb);
13523759911cSjmatthew 		qle_write(sc, QLE_REQ_IN, sc->sc_next_req_id);
13533759911cSjmatthew 		sc->sc_marker_required = 0;
13543759911cSjmatthew 	}
13553759911cSjmatthew 
13563759911cSjmatthew 	req = sc->sc_next_req_id++;
13573759911cSjmatthew 	if (sc->sc_next_req_id == sc->sc_maxcmds)
13583759911cSjmatthew 		sc->sc_next_req_id = 0;
13593759911cSjmatthew 
13603759911cSjmatthew 	offset = (req * QLE_QUEUE_ENTRY_SIZE);
13613759911cSjmatthew 	iocb = QLE_DMA_KVA(sc->sc_requests) + offset;
13623759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests), offset,
13633759911cSjmatthew 	    QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE);
13643759911cSjmatthew 
13653759911cSjmatthew 	ccb->ccb_xs = xs;
13663759911cSjmatthew 
13673759911cSjmatthew 	qle_put_cmd(sc, iocb, xs, ccb, portid);
13683759911cSjmatthew 
13693759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests), offset,
13703759911cSjmatthew 	    QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_PREREAD);
13713759911cSjmatthew 	qle_write(sc, QLE_REQ_IN, sc->sc_next_req_id);
13723759911cSjmatthew 
13733759911cSjmatthew 	if (!ISSET(xs->flags, SCSI_POLL)) {
13743759911cSjmatthew 		mtx_leave(&sc->sc_queue_mtx);
13753759911cSjmatthew 		return;
13763759911cSjmatthew 	}
13773759911cSjmatthew 
1378a1184b53Sjmatthew 	done = 0;
13793759911cSjmatthew 	SIMPLEQ_INIT(&list);
13803759911cSjmatthew 	do {
13813759911cSjmatthew 		u_int16_t isr, info;
1382a1184b53Sjmatthew 		u_int32_t rspin;
13833759911cSjmatthew 		delay(100);
13843759911cSjmatthew 
13853759911cSjmatthew 		if (qle_read_isr(sc, &isr, &info) == 0) {
13863759911cSjmatthew 			continue;
13873759911cSjmatthew 		}
13883759911cSjmatthew 
13893759911cSjmatthew 		if (isr != QLE_INT_TYPE_IO) {
13903759911cSjmatthew 			qle_handle_intr(sc, isr, info);
13913759911cSjmatthew 			continue;
13923759911cSjmatthew 		}
13933759911cSjmatthew 
13943759911cSjmatthew 		rspin = qle_read(sc, QLE_RESP_IN);
1395a1184b53Sjmatthew 		while (rspin != sc->sc_last_resp_id) {
13963759911cSjmatthew 			ccb = qle_handle_resp(sc, sc->sc_last_resp_id);
13973759911cSjmatthew 
13983759911cSjmatthew 			sc->sc_last_resp_id++;
13993759911cSjmatthew 			if (sc->sc_last_resp_id == sc->sc_maxcmds)
14003759911cSjmatthew 				sc->sc_last_resp_id = 0;
14013759911cSjmatthew 
1402a1184b53Sjmatthew 			if (ccb != NULL)
1403a1184b53Sjmatthew 				SIMPLEQ_INSERT_TAIL(&list, ccb, ccb_link);
1404a1184b53Sjmatthew 			if (ccb == xs->io)
1405a1184b53Sjmatthew 				done = 1;
1406a1184b53Sjmatthew 		}
14073759911cSjmatthew 		qle_write(sc, QLE_RESP_OUT, sc->sc_last_resp_id);
14083759911cSjmatthew 		qle_clear_isr(sc, isr);
1409a1184b53Sjmatthew 	} while (done == 0);
14103759911cSjmatthew 
1411a1184b53Sjmatthew 	mtx_leave(&sc->sc_queue_mtx);
1412a1184b53Sjmatthew 
1413a1184b53Sjmatthew 	while ((ccb = SIMPLEQ_FIRST(&list)) != NULL) {
1414a1184b53Sjmatthew 		SIMPLEQ_REMOVE_HEAD(&list, ccb_link);
1415a1184b53Sjmatthew 		scsi_done(ccb->ccb_xs);
1416a1184b53Sjmatthew 	}
14173759911cSjmatthew }
14183759911cSjmatthew 
14193759911cSjmatthew u_int32_t
14203759911cSjmatthew qle_read(struct qle_softc *sc, int offset)
14213759911cSjmatthew {
14223759911cSjmatthew 	u_int32_t v;
14233759911cSjmatthew 	v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset);
14243759911cSjmatthew 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, offset, 4,
14253759911cSjmatthew 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
14263759911cSjmatthew 	return (v);
14273759911cSjmatthew }
14283759911cSjmatthew 
14293759911cSjmatthew void
14303759911cSjmatthew qle_write(struct qle_softc *sc, int offset, u_int32_t value)
14313759911cSjmatthew {
14323759911cSjmatthew 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, value);
14333759911cSjmatthew 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, offset, 4,
14343759911cSjmatthew 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
14353759911cSjmatthew }
14363759911cSjmatthew 
14373759911cSjmatthew u_int16_t
14383759911cSjmatthew qle_read_mbox(struct qle_softc *sc, int mbox)
14393759911cSjmatthew {
14403759911cSjmatthew 	u_int16_t v;
144169e488dfSdlg 	bus_size_t offset = mbox * 2;
144269e488dfSdlg 	v = bus_space_read_2(sc->sc_iot, sc->sc_mbox_ioh, offset);
144369e488dfSdlg 	bus_space_barrier(sc->sc_iot, sc->sc_mbox_ioh, offset, 2,
14443759911cSjmatthew 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
14453759911cSjmatthew 	return (v);
14463759911cSjmatthew }
14473759911cSjmatthew 
14483759911cSjmatthew void
14493759911cSjmatthew qle_write_mbox(struct qle_softc *sc, int mbox, u_int16_t value)
14503759911cSjmatthew {
145169e488dfSdlg 	bus_size_t offset = (mbox * 2);
145269e488dfSdlg 	bus_space_write_2(sc->sc_iot, sc->sc_mbox_ioh, offset, value);
145369e488dfSdlg 	bus_space_barrier(sc->sc_iot, sc->sc_mbox_ioh, offset, 2,
14543759911cSjmatthew 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
14553759911cSjmatthew }
14563759911cSjmatthew 
14573759911cSjmatthew void
14583759911cSjmatthew qle_host_cmd(struct qle_softc *sc, u_int32_t cmd)
14593759911cSjmatthew {
14603759911cSjmatthew 	qle_write(sc, QLE_HOST_CMD_CTRL, cmd << QLE_HOST_CMD_SHIFT);
14613759911cSjmatthew }
14623759911cSjmatthew 
14633759911cSjmatthew #define MBOX_COMMAND_TIMEOUT	400000
14643759911cSjmatthew 
14653759911cSjmatthew int
146665fd2507Sjmatthew qle_mbox(struct qle_softc *sc, int maskin)
14673759911cSjmatthew {
14683759911cSjmatthew 	int i;
14693759911cSjmatthew 	int result = 0;
14703759911cSjmatthew 	int rv;
14713759911cSjmatthew 
14723759911cSjmatthew 	for (i = 0; i < nitems(sc->sc_mbox); i++) {
14733759911cSjmatthew 		if (maskin & (1 << i)) {
14743759911cSjmatthew 			qle_write_mbox(sc, i, sc->sc_mbox[i]);
14753759911cSjmatthew 		}
14763759911cSjmatthew 	}
14773759911cSjmatthew 	qle_host_cmd(sc, QLE_HOST_CMD_SET_HOST_INT);
14783759911cSjmatthew 
147965fd2507Sjmatthew 	if (sc->sc_scsibus != NULL) {
148065fd2507Sjmatthew 		mtx_enter(&sc->sc_mbox_mtx);
148165fd2507Sjmatthew 		sc->sc_mbox_pending = 1;
148265fd2507Sjmatthew 		while (sc->sc_mbox_pending == 1) {
1483541245b6Sjsg 			msleep_nsec(sc->sc_mbox, &sc->sc_mbox_mtx, PRIBIO,
1484541245b6Sjsg 			    "qlembox", INFSLP);
148565fd2507Sjmatthew 		}
148665fd2507Sjmatthew 		result = sc->sc_mbox[0];
148765fd2507Sjmatthew 		sc->sc_mbox_pending = 0;
148865fd2507Sjmatthew 		mtx_leave(&sc->sc_mbox_mtx);
148965fd2507Sjmatthew 		return (result == QLE_MBOX_COMPLETE ? 0 : result);
149065fd2507Sjmatthew 	}
149165fd2507Sjmatthew 
14923759911cSjmatthew 	for (i = 0; i < MBOX_COMMAND_TIMEOUT && result == 0; i++) {
14933759911cSjmatthew 		u_int16_t isr, info;
14943759911cSjmatthew 
14953759911cSjmatthew 		delay(100);
14963759911cSjmatthew 
14973759911cSjmatthew 		if (qle_read_isr(sc, &isr, &info) == 0)
14983759911cSjmatthew 			continue;
14993759911cSjmatthew 
15003759911cSjmatthew 		switch (isr) {
15013759911cSjmatthew 		case QLE_INT_TYPE_MBOX:
15023759911cSjmatthew 			result = info;
15033759911cSjmatthew 			break;
15043759911cSjmatthew 
15053759911cSjmatthew 		default:
15063759911cSjmatthew 			qle_handle_intr(sc, isr, info);
15073759911cSjmatthew 			break;
15083759911cSjmatthew 		}
15093759911cSjmatthew 	}
15103759911cSjmatthew 
151165fd2507Sjmatthew 	if (result == 0) {
15123759911cSjmatthew 		/* timed out; do something? */
151365fd2507Sjmatthew 		DPRINTF(QLE_D_MBOX, "%s: mbox timed out\n", DEVNAME(sc));
15143759911cSjmatthew 		rv = 1;
151565fd2507Sjmatthew 	} else {
151665fd2507Sjmatthew 		for (i = 0; i < nitems(sc->sc_mbox); i++) {
151765fd2507Sjmatthew 			sc->sc_mbox[i] = qle_read_mbox(sc, i);
151865fd2507Sjmatthew 		}
151965fd2507Sjmatthew 		rv = (result == QLE_MBOX_COMPLETE ? 0 : result);
15203759911cSjmatthew 	}
15213759911cSjmatthew 
15223759911cSjmatthew 	qle_clear_isr(sc, QLE_INT_TYPE_MBOX);
15233759911cSjmatthew 	return (rv);
15243759911cSjmatthew }
15253759911cSjmatthew 
15263759911cSjmatthew void
15273759911cSjmatthew qle_mbox_putaddr(u_int16_t *mbox, struct qle_dmamem *mem)
15283759911cSjmatthew {
15293759911cSjmatthew 	mbox[2] = (QLE_DMA_DVA(mem) >> 16) & 0xffff;
15303759911cSjmatthew 	mbox[3] = (QLE_DMA_DVA(mem) >> 0) & 0xffff;
15313759911cSjmatthew 	mbox[6] = (QLE_DMA_DVA(mem) >> 48) & 0xffff;
15323759911cSjmatthew 	mbox[7] = (QLE_DMA_DVA(mem) >> 32) & 0xffff;
15333759911cSjmatthew }
15343759911cSjmatthew 
15353759911cSjmatthew void
15363759911cSjmatthew qle_set_ints(struct qle_softc *sc, int enabled)
15373759911cSjmatthew {
15383759911cSjmatthew 	u_int32_t v = enabled ? QLE_INT_CTRL_ENABLE : 0;
15393759911cSjmatthew 	qle_write(sc, QLE_INT_CTRL, v);
15403759911cSjmatthew }
15413759911cSjmatthew 
15423759911cSjmatthew int
15433759911cSjmatthew qle_read_isr(struct qle_softc *sc, u_int16_t *isr, u_int16_t *info)
15443759911cSjmatthew {
15453759911cSjmatthew 	u_int32_t v;
15463759911cSjmatthew 
15473759911cSjmatthew 	switch (sc->sc_isp_gen) {
15483759911cSjmatthew 	case QLE_GEN_ISP24XX:
1549b803df0bSjmatthew 	case QLE_GEN_ISP25XX:
15503759911cSjmatthew 		if ((qle_read(sc, QLE_INT_STATUS) & QLE_RISC_INT_REQ) == 0)
15513759911cSjmatthew 			return (0);
15523759911cSjmatthew 
15533759911cSjmatthew 		v = qle_read(sc, QLE_RISC_STATUS);
15543759911cSjmatthew 
15553759911cSjmatthew 		switch (v & QLE_INT_STATUS_MASK) {
15563759911cSjmatthew 		case QLE_24XX_INT_ROM_MBOX:
15573759911cSjmatthew 		case QLE_24XX_INT_ROM_MBOX_FAIL:
15583759911cSjmatthew 		case QLE_24XX_INT_MBOX:
15593759911cSjmatthew 		case QLE_24XX_INT_MBOX_FAIL:
15603759911cSjmatthew 			*isr = QLE_INT_TYPE_MBOX;
15613759911cSjmatthew 			break;
15623759911cSjmatthew 
15633759911cSjmatthew 		case QLE_24XX_INT_ASYNC:
15643759911cSjmatthew 			*isr = QLE_INT_TYPE_ASYNC;
15653759911cSjmatthew 			break;
15663759911cSjmatthew 
15673759911cSjmatthew 		case QLE_24XX_INT_RSPQ:
15683759911cSjmatthew 			*isr = QLE_INT_TYPE_IO;
15693759911cSjmatthew 			break;
15703759911cSjmatthew 
15713759911cSjmatthew 		default:
15723759911cSjmatthew 			*isr = QLE_INT_TYPE_OTHER;
15733759911cSjmatthew 			break;
15743759911cSjmatthew 		}
15753759911cSjmatthew 
15763759911cSjmatthew 		*info = (v >> QLE_INT_INFO_SHIFT);
15773759911cSjmatthew 		return (1);
15783759911cSjmatthew 
15793759911cSjmatthew 	default:
15803759911cSjmatthew 		return (0);
15813759911cSjmatthew 	}
15823759911cSjmatthew }
15833759911cSjmatthew 
15843759911cSjmatthew void
15853759911cSjmatthew qle_clear_isr(struct qle_softc *sc, u_int16_t isr)
15863759911cSjmatthew {
15873759911cSjmatthew 	qle_host_cmd(sc, QLE_HOST_CMD_CLR_RISC_INT);
15883759911cSjmatthew }
15893759911cSjmatthew 
15903759911cSjmatthew void
15913759911cSjmatthew qle_update_done(struct qle_softc *sc, int task)
15923759911cSjmatthew {
15933759911cSjmatthew 	atomic_clearbits_int(&sc->sc_update_tasks, task);
15943759911cSjmatthew }
15953759911cSjmatthew 
15963759911cSjmatthew void
15973fc6fae1Sjmatthew qle_update_cancel(struct qle_softc *sc)
15983fc6fae1Sjmatthew {
15993fc6fae1Sjmatthew 	atomic_swap_uint(&sc->sc_update_tasks, 0);
16003fc6fae1Sjmatthew 	timeout_del(&sc->sc_update_timeout);
16013fc6fae1Sjmatthew 	task_del(sc->sc_update_taskq, &sc->sc_update_task);
16023fc6fae1Sjmatthew }
16033fc6fae1Sjmatthew 
16043fc6fae1Sjmatthew void
16053759911cSjmatthew qle_update_start(struct qle_softc *sc, int task)
16063759911cSjmatthew {
16073759911cSjmatthew 	atomic_setbits_int(&sc->sc_update_tasks, task);
16083fc6fae1Sjmatthew 	if (!timeout_pending(&sc->sc_update_timeout))
16093759911cSjmatthew 		task_add(sc->sc_update_taskq, &sc->sc_update_task);
16103759911cSjmatthew }
16113759911cSjmatthew 
16123759911cSjmatthew void
16133fc6fae1Sjmatthew qle_update_defer(struct qle_softc *sc, int task)
16143fc6fae1Sjmatthew {
16153fc6fae1Sjmatthew 	atomic_setbits_int(&sc->sc_update_tasks, task);
16163fc6fae1Sjmatthew 	timeout_del(&sc->sc_update_timeout);
16173fc6fae1Sjmatthew 	task_del(sc->sc_update_taskq, &sc->sc_update_task);
16183fc6fae1Sjmatthew 	timeout_add_msec(&sc->sc_update_timeout, QLE_LOOP_SETTLE);
16193fc6fae1Sjmatthew }
16203fc6fae1Sjmatthew 
16213fc6fae1Sjmatthew void
16223759911cSjmatthew qle_clear_port_lists(struct qle_softc *sc)
16233759911cSjmatthew {
1624c9d76b5bSderaadt 	struct qle_fc_port *port;
16253759911cSjmatthew 	while (!TAILQ_EMPTY(&sc->sc_ports_found)) {
1626c9d76b5bSderaadt 		port = TAILQ_FIRST(&sc->sc_ports_found);
1627c9d76b5bSderaadt 		TAILQ_REMOVE(&sc->sc_ports_found, port, update);
1628c9d76b5bSderaadt 		free(port, M_DEVBUF, sizeof *port);
16293759911cSjmatthew 	}
16303759911cSjmatthew 
16313759911cSjmatthew 	while (!TAILQ_EMPTY(&sc->sc_ports_new)) {
1632c9d76b5bSderaadt 		port = TAILQ_FIRST(&sc->sc_ports_new);
1633c9d76b5bSderaadt 		TAILQ_REMOVE(&sc->sc_ports_new, port, update);
1634c9d76b5bSderaadt 		free(port, M_DEVBUF, sizeof *port);
16353759911cSjmatthew 	}
16363759911cSjmatthew 
16373759911cSjmatthew 	while (!TAILQ_EMPTY(&sc->sc_ports_gone)) {
1638c9d76b5bSderaadt 		port = TAILQ_FIRST(&sc->sc_ports_gone);
1639c9d76b5bSderaadt 		TAILQ_REMOVE(&sc->sc_ports_gone, port, update);
16403759911cSjmatthew 	}
16413759911cSjmatthew }
16423759911cSjmatthew 
16433759911cSjmatthew int
16443759911cSjmatthew qle_softreset(struct qle_softc *sc)
16453759911cSjmatthew {
16463759911cSjmatthew 	int i;
16473759911cSjmatthew 	qle_set_ints(sc, 0);
16483759911cSjmatthew 
16493759911cSjmatthew 	/* set led control bits, stop dma */
16503759911cSjmatthew 	qle_write(sc, QLE_GPIO_DATA, 0);
16513759911cSjmatthew 	qle_write(sc, QLE_CTRL_STATUS, QLE_CTRL_DMA_SHUTDOWN);
16523759911cSjmatthew 	while (qle_read(sc, QLE_CTRL_STATUS) & QLE_CTRL_DMA_ACTIVE) {
1653cc014dc0Sjmatthew 		DPRINTF(QLE_D_IO, "%s: dma still active\n", DEVNAME(sc));
16543759911cSjmatthew 		delay(100);
16553759911cSjmatthew 	}
16563759911cSjmatthew 
16573759911cSjmatthew 	/* reset */
16583759911cSjmatthew 	qle_write(sc, QLE_CTRL_STATUS, QLE_CTRL_RESET | QLE_CTRL_DMA_SHUTDOWN);
16593759911cSjmatthew 	delay(100);
16603759911cSjmatthew 	/* clear data and control dma engines? */
16613759911cSjmatthew 
16623759911cSjmatthew 	/* wait for soft reset to clear */
16633759911cSjmatthew 	for (i = 0; i < 1000; i++) {
16643759911cSjmatthew 		if (qle_read_mbox(sc, 0) == 0x0000)
16653759911cSjmatthew 			break;
16663759911cSjmatthew 
16673759911cSjmatthew 		delay(100);
16683759911cSjmatthew 	}
16693759911cSjmatthew 
16703759911cSjmatthew 	if (i == 1000) {
16713759911cSjmatthew 		printf("%s: reset mbox didn't clear\n", DEVNAME(sc));
16723759911cSjmatthew 		qle_set_ints(sc, 0);
16733759911cSjmatthew 		return (ENXIO);
16743759911cSjmatthew 	}
16753759911cSjmatthew 
16763759911cSjmatthew 	for (i = 0; i < 500000; i++) {
16773759911cSjmatthew 		if ((qle_read(sc, QLE_CTRL_STATUS) & QLE_CTRL_RESET) == 0)
16783759911cSjmatthew 			break;
16793759911cSjmatthew 		delay(5);
16803759911cSjmatthew 	}
16813759911cSjmatthew 	if (i == 500000) {
16823759911cSjmatthew 		printf("%s: reset status didn't clear\n", DEVNAME(sc));
16833759911cSjmatthew 		return (ENXIO);
16843759911cSjmatthew 	}
16853759911cSjmatthew 
16863759911cSjmatthew 	/* reset risc processor */
16873759911cSjmatthew 	qle_host_cmd(sc, QLE_HOST_CMD_RESET);
16883759911cSjmatthew 	qle_host_cmd(sc, QLE_HOST_CMD_RELEASE);
16893759911cSjmatthew 	qle_host_cmd(sc, QLE_HOST_CMD_CLEAR_RESET);
16903759911cSjmatthew 
16913759911cSjmatthew 	/* wait for reset to clear */
16923759911cSjmatthew 	for (i = 0; i < 1000; i++) {
16933759911cSjmatthew 		if (qle_read_mbox(sc, 0) == 0x0000)
16943759911cSjmatthew 			break;
16953759911cSjmatthew 		delay(100);
16963759911cSjmatthew 	}
16973759911cSjmatthew 	if (i == 1000) {
16983759911cSjmatthew 		printf("%s: risc not ready after reset\n", DEVNAME(sc));
16993759911cSjmatthew 		return (ENXIO);
17003759911cSjmatthew 	}
17013759911cSjmatthew 
17023759911cSjmatthew 	/* reset queue pointers */
17033759911cSjmatthew 	qle_write(sc, QLE_REQ_IN, 0);
17043759911cSjmatthew 	qle_write(sc, QLE_REQ_OUT, 0);
17053759911cSjmatthew 	qle_write(sc, QLE_RESP_IN, 0);
17063759911cSjmatthew 	qle_write(sc, QLE_RESP_OUT, 0);
17073759911cSjmatthew 
17083759911cSjmatthew 	qle_set_ints(sc, 1);
17093759911cSjmatthew 
17103759911cSjmatthew 	/* do a basic mailbox operation to check we're alive */
17113759911cSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_NOP;
171265fd2507Sjmatthew 	if (qle_mbox(sc, 0x0001)) {
17133759911cSjmatthew 		printf("ISP not responding after reset\n");
17143759911cSjmatthew 		return (ENXIO);
17153759911cSjmatthew 	}
17163759911cSjmatthew 
17173759911cSjmatthew 	return (0);
17183759911cSjmatthew }
17193759911cSjmatthew 
17203759911cSjmatthew void
17213759911cSjmatthew qle_update_topology(struct qle_softc *sc)
17223759911cSjmatthew {
17233759911cSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_GET_ID;
172465fd2507Sjmatthew 	if (qle_mbox(sc, 0x0001)) {
1725cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: unable to get loop id\n", DEVNAME(sc));
17263759911cSjmatthew 		sc->sc_topology = QLE_TOPO_N_PORT_NO_TARGET;
17273759911cSjmatthew 	} else {
17283759911cSjmatthew 		sc->sc_topology = sc->sc_mbox[6];
17293759911cSjmatthew 		sc->sc_loop_id = sc->sc_mbox[1];
17303759911cSjmatthew 
17313759911cSjmatthew 		switch (sc->sc_topology) {
17323759911cSjmatthew 		case QLE_TOPO_NL_PORT:
17333759911cSjmatthew 		case QLE_TOPO_N_PORT:
1734cc014dc0Sjmatthew 			DPRINTF(QLE_D_PORT, "%s: loop id %d\n", DEVNAME(sc),
17353759911cSjmatthew 			    sc->sc_loop_id);
17363759911cSjmatthew 			break;
17373759911cSjmatthew 
17383759911cSjmatthew 		case QLE_TOPO_FL_PORT:
17393759911cSjmatthew 		case QLE_TOPO_F_PORT:
17403759911cSjmatthew 			sc->sc_port_id = sc->sc_mbox[2] |
17413759911cSjmatthew 			    (sc->sc_mbox[3] << 16);
1742cc014dc0Sjmatthew 			DPRINTF(QLE_D_PORT, "%s: fabric port id %06x\n",
1743cc014dc0Sjmatthew 			    DEVNAME(sc), sc->sc_port_id);
17443759911cSjmatthew 			break;
17453759911cSjmatthew 
17463759911cSjmatthew 		case QLE_TOPO_N_PORT_NO_TARGET:
17473759911cSjmatthew 		default:
1748cc014dc0Sjmatthew 			DPRINTF(QLE_D_PORT, "%s: not useful\n", DEVNAME(sc));
17493759911cSjmatthew 			break;
17503759911cSjmatthew 		}
17513759911cSjmatthew 
17523759911cSjmatthew 		switch (sc->sc_topology) {
17533759911cSjmatthew 		case QLE_TOPO_NL_PORT:
17543759911cSjmatthew 		case QLE_TOPO_FL_PORT:
17553759911cSjmatthew 			sc->sc_loop_max_id = 126;
17563759911cSjmatthew 			break;
17573759911cSjmatthew 
17583759911cSjmatthew 		case QLE_TOPO_N_PORT:
17593759911cSjmatthew 			sc->sc_loop_max_id = 2;
17603759911cSjmatthew 			break;
17613759911cSjmatthew 
17623759911cSjmatthew 		default:
17633759911cSjmatthew 			sc->sc_loop_max_id = 0;
17643759911cSjmatthew 			break;
17653759911cSjmatthew 		}
17663759911cSjmatthew 	}
17673759911cSjmatthew }
17683759911cSjmatthew 
17693759911cSjmatthew int
17703759911cSjmatthew qle_update_fabric(struct qle_softc *sc)
17713759911cSjmatthew {
17723759911cSjmatthew 	/*struct qle_sns_rft_id *rft;*/
17733759911cSjmatthew 
17743759911cSjmatthew 	switch (sc->sc_topology) {
17753759911cSjmatthew 	case QLE_TOPO_F_PORT:
17763759911cSjmatthew 	case QLE_TOPO_FL_PORT:
17773759911cSjmatthew 		break;
17783759911cSjmatthew 
17793759911cSjmatthew 	default:
17803759911cSjmatthew 		return (0);
17813759911cSjmatthew 	}
17823759911cSjmatthew 
17833759911cSjmatthew 	/* get the name server's port db entry */
17843759911cSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_GET_PORT_DB;
17853759911cSjmatthew 	sc->sc_mbox[1] = QLE_F_PORT_HANDLE;
17863759911cSjmatthew 	qle_mbox_putaddr(sc->sc_mbox, sc->sc_scratch);
17873759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0,
17883759911cSjmatthew 	    sizeof(struct qle_get_port_db), BUS_DMASYNC_PREREAD);
178965fd2507Sjmatthew 	if (qle_mbox(sc, 0x00cf)) {
1790cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: get port db for SNS failed: %x\n",
17913759911cSjmatthew 		    DEVNAME(sc), sc->sc_mbox[0]);
17923759911cSjmatthew 		sc->sc_sns_port_name = 0;
17933759911cSjmatthew 	} else {
17943759911cSjmatthew 		struct qle_get_port_db *pdb;
17953759911cSjmatthew 		bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0,
17963759911cSjmatthew 		    sizeof(struct qle_get_port_db), BUS_DMASYNC_POSTREAD);
17973759911cSjmatthew 		pdb = QLE_DMA_KVA(sc->sc_scratch);
1798cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: SNS port name %llx\n", DEVNAME(sc),
17993759911cSjmatthew 		    betoh64(pdb->port_name));
18003759911cSjmatthew 		sc->sc_sns_port_name = betoh64(pdb->port_name);
18013759911cSjmatthew 	}
18023759911cSjmatthew 
18033759911cSjmatthew 	/*
18043759911cSjmatthew 	 * register fc4 types with the fabric
18053759911cSjmatthew 	 * some switches do this automatically, but apparently
18063759911cSjmatthew 	 * some don't.
18073759911cSjmatthew 	 */
18083759911cSjmatthew 	/*
18093759911cSjmatthew 	rft = QLE_DMA_KVA(sc->sc_scratch);
18103759911cSjmatthew 	memset(rft, 0, sizeof(*rft) + sizeof(struct qle_sns_req_hdr));
181161ff084cSdlg 	htolem16(&rft->subcmd, QLE_SNS_RFT_ID);
181261ff084cSdlg 	htolem16(&rft->max_word, sizeof(struct qle_sns_req_hdr) / 4);
181361ff084cSdlg 	htolem32(&rft->port_id, sc->sc_port_id);
18143759911cSjmatthew 	rft->fc4_types[0] = (1 << QLE_FC4_SCSI);
18153759911cSjmatthew 	if (qle_sns_req(sc, sc->sc_scratch, sizeof(*rft))) {
18163759911cSjmatthew 		printf("%s: RFT_ID failed\n", DEVNAME(sc));
18173759911cSjmatthew 		/ * we might be able to continue after this fails * /
18183759911cSjmatthew 	}
18193759911cSjmatthew 	*/
18203759911cSjmatthew 
18213759911cSjmatthew 	return (1);
18223759911cSjmatthew }
18233759911cSjmatthew 
18243759911cSjmatthew int
18253759911cSjmatthew qle_ct_pass_through(struct qle_softc *sc, u_int32_t port_handle,
18263759911cSjmatthew     struct qle_dmamem *mem, size_t req_size, size_t resp_size)
18273759911cSjmatthew {
18283759911cSjmatthew 	struct qle_iocb_ct_passthrough *iocb;
18293759911cSjmatthew 	u_int16_t req;
18303759911cSjmatthew 	u_int64_t offset;
18313759911cSjmatthew 	int rv;
18323759911cSjmatthew 
18333759911cSjmatthew 	mtx_enter(&sc->sc_queue_mtx);
18343759911cSjmatthew 
18353759911cSjmatthew 	req = sc->sc_next_req_id++;
18363759911cSjmatthew 	if (sc->sc_next_req_id == sc->sc_maxcmds)
18373759911cSjmatthew 		sc->sc_next_req_id = 0;
18383759911cSjmatthew 
18393759911cSjmatthew 	offset = (req * QLE_QUEUE_ENTRY_SIZE);
18403759911cSjmatthew 	iocb = QLE_DMA_KVA(sc->sc_requests) + offset;
18413759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests), offset,
18423759911cSjmatthew 	    QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE);
18433759911cSjmatthew 
18443759911cSjmatthew 	memset(iocb, 0, QLE_QUEUE_ENTRY_SIZE);
18453759911cSjmatthew 	iocb->entry_type = QLE_IOCB_CT_PASSTHROUGH;
18463759911cSjmatthew 	iocb->entry_count = 1;
18473759911cSjmatthew 
18483759911cSjmatthew 	iocb->req_handle = 9;
184961ff084cSdlg 	htolem16(&iocb->req_nport_handle, port_handle);
185061ff084cSdlg 	htolem16(&iocb->req_dsd_count, 1);
185161ff084cSdlg 	htolem16(&iocb->req_resp_dsd_count, 1);
185261ff084cSdlg 	htolem32(&iocb->req_cmd_byte_count, req_size);
185361ff084cSdlg 	htolem32(&iocb->req_resp_byte_count, resp_size);
1854a0abcb68Sdlg 	qle_sge(&iocb->req_cmd_seg, QLE_DMA_DVA(mem), req_size);
1855a0abcb68Sdlg 	qle_sge(&iocb->req_resp_seg, QLE_DMA_DVA(mem) + req_size, resp_size);
18563759911cSjmatthew 
18573759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0, QLE_DMA_LEN(mem),
18583759911cSjmatthew 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
18593759911cSjmatthew 	qle_write(sc, QLE_REQ_IN, sc->sc_next_req_id);
18603759911cSjmatthew 	sc->sc_fabric_pending = 1;
18613759911cSjmatthew 	mtx_leave(&sc->sc_queue_mtx);
18623759911cSjmatthew 
18633759911cSjmatthew 	/* maybe put a proper timeout on this */
18643759911cSjmatthew 	rv = 0;
18653759911cSjmatthew 	while (sc->sc_fabric_pending == 1) {
18663759911cSjmatthew 		if (sc->sc_scsibus == NULL) {
18673759911cSjmatthew 			u_int16_t isr, info;
18683759911cSjmatthew 
18693759911cSjmatthew 			delay(100);
18703759911cSjmatthew 			if (qle_read_isr(sc, &isr, &info) != 0)
18713759911cSjmatthew 				qle_handle_intr(sc, isr, info);
18723759911cSjmatthew 		} else {
1873487476ffScheloha 			tsleep_nsec(sc->sc_scratch, PRIBIO, "qle_fabric",
1874487476ffScheloha 			    SEC_TO_NSEC(1));
18753759911cSjmatthew 		}
18763759911cSjmatthew 	}
18773759911cSjmatthew 	if (rv == 0)
18783759911cSjmatthew 		bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0,
18793759911cSjmatthew 		    QLE_DMA_LEN(mem), BUS_DMASYNC_POSTREAD |
18803759911cSjmatthew 		    BUS_DMASYNC_POSTWRITE);
18813759911cSjmatthew 
18823759911cSjmatthew 	sc->sc_fabric_pending = 0;
18833759911cSjmatthew 
18843759911cSjmatthew 	return (rv);
18853759911cSjmatthew }
18863759911cSjmatthew 
18873759911cSjmatthew struct qle_fc_port *
18883759911cSjmatthew qle_next_fabric_port(struct qle_softc *sc, u_int32_t *firstport,
18893759911cSjmatthew     u_int32_t *lastport)
18903759911cSjmatthew {
18913759911cSjmatthew 	struct qle_ct_ga_nxt_req *ga;
18923759911cSjmatthew 	struct qle_ct_ga_nxt_resp *gar;
18933759911cSjmatthew 	struct qle_fc_port *fport;
18943759911cSjmatthew 	int result;
18953759911cSjmatthew 
18963759911cSjmatthew 	/* get the next port from the fabric nameserver */
18973759911cSjmatthew 	ga = QLE_DMA_KVA(sc->sc_scratch);
18983759911cSjmatthew 	memset(ga, 0, sizeof(*ga) + sizeof(*gar));
18993759911cSjmatthew 	ga->header.ct_revision = 0x01;
19003759911cSjmatthew 	ga->header.ct_gs_type = 0xfc;
19013759911cSjmatthew 	ga->header.ct_gs_subtype = 0x02;
19023759911cSjmatthew 	ga->subcmd = htobe16(QLE_SNS_GA_NXT);
19033759911cSjmatthew 	ga->max_word = htobe16((sizeof(*gar) - 16) / 4);
19043759911cSjmatthew 	ga->port_id = htobe32(*lastport);
19053759911cSjmatthew 	result = qle_ct_pass_through(sc, QLE_SNS_HANDLE, sc->sc_scratch,
19063759911cSjmatthew 	    sizeof(*ga), sizeof(*gar));
19073759911cSjmatthew 	if (result) {
1908871bea04Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: GA_NXT %06x failed: %x\n", DEVNAME(sc),
1909871bea04Sjmatthew 		    *lastport, result);
19103759911cSjmatthew 		*lastport = 0xffffffff;
19113759911cSjmatthew 		return (NULL);
19123759911cSjmatthew 	}
19133759911cSjmatthew 
19143759911cSjmatthew 	gar = (struct qle_ct_ga_nxt_resp *)(ga + 1);
19153759911cSjmatthew 	/* if the response is all zeroes, try again */
19163759911cSjmatthew 	if (gar->port_type_id == 0 && gar->port_name == 0 &&
19173759911cSjmatthew 	    gar->node_name == 0) {
1918cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: GA_NXT returned junk\n", DEVNAME(sc));
19193759911cSjmatthew 		return (NULL);
19203759911cSjmatthew 	}
19213759911cSjmatthew 
19223759911cSjmatthew 	/* are we back at the start? */
19233759911cSjmatthew 	*lastport = betoh32(gar->port_type_id) & 0xffffff;
19243759911cSjmatthew 	if (*lastport == *firstport) {
19253759911cSjmatthew 		*lastport = 0xffffffff;
19263759911cSjmatthew 		return (NULL);
19273759911cSjmatthew 	}
19283759911cSjmatthew 	if (*firstport == 0xffffffff)
19293759911cSjmatthew 		*firstport = *lastport;
19303759911cSjmatthew 
1931871bea04Sjmatthew 	DPRINTF(QLE_D_PORT, "%s: GA_NXT: port id: %06x, wwpn %llx, wwnn %llx\n",
1932871bea04Sjmatthew 	    DEVNAME(sc), *lastport, betoh64(gar->port_name),
19333759911cSjmatthew 	    betoh64(gar->node_name));
19343759911cSjmatthew 
19353759911cSjmatthew 	/* don't try to log in to ourselves */
19363759911cSjmatthew 	if (*lastport == sc->sc_port_id) {
19373759911cSjmatthew 		return (NULL);
19383759911cSjmatthew 	}
19393759911cSjmatthew 
19403759911cSjmatthew 	fport = malloc(sizeof(*fport), M_DEVBUF, M_ZERO | M_NOWAIT);
19413759911cSjmatthew 	if (fport == NULL) {
19423759911cSjmatthew 		printf("%s: failed to allocate a port struct\n",
19433759911cSjmatthew 		    DEVNAME(sc));
19443759911cSjmatthew 		*lastport = 0xffffffff;
19453759911cSjmatthew 		return (NULL);
19463759911cSjmatthew 	}
19473759911cSjmatthew 	fport->port_name = betoh64(gar->port_name);
19483759911cSjmatthew 	fport->node_name = betoh64(gar->node_name);
19493759911cSjmatthew 	fport->location = QLE_LOCATION_PORT_ID(*lastport);
19503759911cSjmatthew 	fport->portid = *lastport;
19513759911cSjmatthew 	return (fport);
19523759911cSjmatthew }
19533759911cSjmatthew 
19543759911cSjmatthew int
1955e5d3c726Sjmatthew qle_fabric_plogx(struct qle_softc *sc, struct qle_fc_port *port, int flags,
1956e5d3c726Sjmatthew     u_int32_t *info)
19573759911cSjmatthew {
19583759911cSjmatthew 	struct qle_iocb_plogx *iocb;
19593759911cSjmatthew 	u_int16_t req;
19603759911cSjmatthew 	u_int64_t offset;
19613759911cSjmatthew 	int rv;
19623759911cSjmatthew 
19633759911cSjmatthew 	mtx_enter(&sc->sc_queue_mtx);
19643759911cSjmatthew 
19653759911cSjmatthew 	req = sc->sc_next_req_id++;
19663759911cSjmatthew 	if (sc->sc_next_req_id == sc->sc_maxcmds)
19673759911cSjmatthew 		sc->sc_next_req_id = 0;
19683759911cSjmatthew 
19693759911cSjmatthew 	offset = (req * QLE_QUEUE_ENTRY_SIZE);
19703759911cSjmatthew 	iocb = QLE_DMA_KVA(sc->sc_requests) + offset;
19713759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests), offset,
19723759911cSjmatthew 	    QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE);
19733759911cSjmatthew 
19743759911cSjmatthew 	memset(iocb, 0, QLE_QUEUE_ENTRY_SIZE);
19753759911cSjmatthew 	iocb->entry_type = QLE_IOCB_PLOGX;
19763759911cSjmatthew 	iocb->entry_count = 1;
19773759911cSjmatthew 
19783759911cSjmatthew 	iocb->req_handle = 7;
1979e5d3c726Sjmatthew 	htolem16(&iocb->req_nport_handle, port->loopid);
198061ff084cSdlg 	htolem16(&iocb->req_port_id_lo, port->portid);
198161ff084cSdlg 	iocb->req_port_id_hi = port->portid >> 16;
1982e5d3c726Sjmatthew 	htolem16(&iocb->req_flags, flags);
19833759911cSjmatthew 
1984e5d3c726Sjmatthew 	DPRINTF(QLE_D_PORT, "%s: plogx loop id %d port %06x, flags %x\n",
1985e5d3c726Sjmatthew 	    DEVNAME(sc), port->loopid, port->portid, flags);
1986e5d3c726Sjmatthew 	qle_dump_iocb(sc, iocb);
19873759911cSjmatthew 
19883759911cSjmatthew 	qle_write(sc, QLE_REQ_IN, sc->sc_next_req_id);
19893759911cSjmatthew 	sc->sc_fabric_pending = 1;
19903759911cSjmatthew 	mtx_leave(&sc->sc_queue_mtx);
19913759911cSjmatthew 
19923759911cSjmatthew 	/* maybe put a proper timeout on this */
19933759911cSjmatthew 	rv = 0;
19943759911cSjmatthew 	while (sc->sc_fabric_pending == 1) {
19953759911cSjmatthew 		if (sc->sc_scsibus == NULL) {
19963759911cSjmatthew 			u_int16_t isr, info;
19973759911cSjmatthew 
19983759911cSjmatthew 			delay(100);
19993759911cSjmatthew 			if (qle_read_isr(sc, &isr, &info) != 0)
20003759911cSjmatthew 				qle_handle_intr(sc, isr, info);
20013759911cSjmatthew 		} else {
2002487476ffScheloha 			tsleep_nsec(sc->sc_scratch, PRIBIO, "qle_fabric",
2003487476ffScheloha 			    SEC_TO_NSEC(1));
20043759911cSjmatthew 		}
20053759911cSjmatthew 	}
20063759911cSjmatthew 	sc->sc_fabric_pending = 0;
20073759911cSjmatthew 
2008e5d3c726Sjmatthew 	iocb = (struct qle_iocb_plogx *)&sc->sc_fabric_response;
2009e5d3c726Sjmatthew 	rv = lemtoh16(&iocb->req_status);
2010e5d3c726Sjmatthew 	if (rv == QLE_PLOGX_ERROR) {
2011e5d3c726Sjmatthew 		rv = lemtoh32(&iocb->req_ioparms[0]);
2012e5d3c726Sjmatthew 		*info = lemtoh32(&iocb->req_ioparms[1]);
2013e5d3c726Sjmatthew 	}
2014e5d3c726Sjmatthew 
20153759911cSjmatthew 	return (rv);
20163759911cSjmatthew }
20173759911cSjmatthew 
2018e5d3c726Sjmatthew int
2019e5d3c726Sjmatthew qle_fabric_plogi(struct qle_softc *sc, struct qle_fc_port *port)
2020e5d3c726Sjmatthew {
2021e5d3c726Sjmatthew 	u_int32_t info;
20220d6eccfaSjmatthew 	int err, loopid;
2023e5d3c726Sjmatthew 
20240d6eccfaSjmatthew 	loopid = 0;
2025b9154820Sjmatthew retry:
2026e5d3c726Sjmatthew 	if (port->loopid == 0) {
2027e5d3c726Sjmatthew 
2028e5d3c726Sjmatthew 		mtx_enter(&sc->sc_port_mtx);
20290d6eccfaSjmatthew 		loopid = qle_get_loop_id(sc, loopid);
2030e5d3c726Sjmatthew 		mtx_leave(&sc->sc_port_mtx);
2031e5d3c726Sjmatthew 		if (loopid == -1) {
2032e5d3c726Sjmatthew 			printf("%s: ran out of loop ids\n", DEVNAME(sc));
2033e5d3c726Sjmatthew 			return (1);
2034e5d3c726Sjmatthew 		}
2035e5d3c726Sjmatthew 
2036e5d3c726Sjmatthew 		port->loopid = loopid;
2037e5d3c726Sjmatthew 	}
2038e5d3c726Sjmatthew 
2039e5d3c726Sjmatthew 	err = qle_fabric_plogx(sc, port, QLE_PLOGX_LOGIN, &info);
2040580dbe8fSjmatthew 	switch (err) {
2041580dbe8fSjmatthew 	case 0:
2042580dbe8fSjmatthew 		DPRINTF(QLE_D_PORT, "%s: logged in to %06x as %d\n",
2043e5d3c726Sjmatthew 		    DEVNAME(sc), port->portid, port->loopid);
2044e5d3c726Sjmatthew 		port->flags &= ~QLE_PORT_FLAG_NEEDS_LOGIN;
2045e5d3c726Sjmatthew 		return (0);
2046580dbe8fSjmatthew 
2047580dbe8fSjmatthew 	case QLE_PLOGX_ERROR_PORT_ID_USED:
2048580dbe8fSjmatthew 		DPRINTF(QLE_D_PORT, "%s: already logged in to %06x as %d\n",
2049580dbe8fSjmatthew 		    DEVNAME(sc), port->portid, info);
2050580dbe8fSjmatthew 		port->loopid = info;
2051b9154820Sjmatthew 		port->flags &= ~QLE_PORT_FLAG_NEEDS_LOGIN;
2052580dbe8fSjmatthew 		return (0);
2053580dbe8fSjmatthew 
2054580dbe8fSjmatthew 	case QLE_PLOGX_ERROR_HANDLE_USED:
20550d6eccfaSjmatthew 		if (qle_add_logged_in_port(sc, loopid, info)) {
2056b9154820Sjmatthew 			return (1);
2057b9154820Sjmatthew 		}
2058b9154820Sjmatthew 		port->loopid = 0;
20590d6eccfaSjmatthew 		loopid++;
2060b9154820Sjmatthew 		goto retry;
2061580dbe8fSjmatthew 
2062580dbe8fSjmatthew 	default:
2063580dbe8fSjmatthew 		DPRINTF(QLE_D_PORT, "%s: error %x logging in to port %06x\n",
2064b9154820Sjmatthew 		    DEVNAME(sc), err, port->portid);
2065e5d3c726Sjmatthew 		port->loopid = 0;
2066e5d3c726Sjmatthew 		return (1);
2067e5d3c726Sjmatthew 	}
2068e5d3c726Sjmatthew }
2069e5d3c726Sjmatthew 
20703759911cSjmatthew void
20713759911cSjmatthew qle_fabric_plogo(struct qle_softc *sc, struct qle_fc_port *port)
20723759911cSjmatthew {
2073e5d3c726Sjmatthew 	int err;
2074e5d3c726Sjmatthew 	u_int32_t info;
20753759911cSjmatthew 
2076e5d3c726Sjmatthew 	/*
2077e5d3c726Sjmatthew 	 * we only log out if we can't see the port any more, so we always
2078e5d3c726Sjmatthew 	 * want to do an explicit logout and free the n-port handle.
2079e5d3c726Sjmatthew 	 */
2080e5d3c726Sjmatthew 	err = qle_fabric_plogx(sc, port, QLE_PLOGX_LOGOUT |
2081e5d3c726Sjmatthew 	    QLE_PLOGX_LOGOUT_EXPLICIT | QLE_PLOGX_LOGOUT_FREE_HANDLE, &info);
2082e5d3c726Sjmatthew 	if (err == 0) {
2083e5d3c726Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: logged out of port %06x\n",
2084e5d3c726Sjmatthew 		    DEVNAME(sc), port->portid);
2085e5d3c726Sjmatthew 	} else {
2086e5d3c726Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: failed to log out of port %06x: "
2087e5d3c726Sjmatthew 		    "%x %x\n", DEVNAME(sc), port->portid, err, info);
2088e5d3c726Sjmatthew 	}
20893759911cSjmatthew }
20903759911cSjmatthew 
20913759911cSjmatthew void
20923fc6fae1Sjmatthew qle_deferred_update(void *xsc)
20933fc6fae1Sjmatthew {
20943fc6fae1Sjmatthew 	struct qle_softc *sc = xsc;
20953fc6fae1Sjmatthew 	task_add(sc->sc_update_taskq, &sc->sc_update_task);
20963fc6fae1Sjmatthew }
20973fc6fae1Sjmatthew 
20983fc6fae1Sjmatthew void
2099e4195480Sdlg qle_do_update(void *xsc)
21003759911cSjmatthew {
21013759911cSjmatthew 	struct qle_softc *sc = xsc;
2102580dbe8fSjmatthew 	int firstport, lastport;
2103580dbe8fSjmatthew 	struct qle_fc_port *port, *fport;
21043759911cSjmatthew 
2105cc014dc0Sjmatthew 	DPRINTF(QLE_D_PORT, "%s: updating\n", DEVNAME(sc));
21063759911cSjmatthew 	while (sc->sc_update_tasks != 0) {
21073759911cSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_CLEAR_ALL) {
21083759911cSjmatthew 			TAILQ_HEAD(, qle_fc_port) detach;
2109cc014dc0Sjmatthew 			DPRINTF(QLE_D_PORT, "%s: detaching everything\n",
2110cc014dc0Sjmatthew 			    DEVNAME(sc));
21113759911cSjmatthew 
21123759911cSjmatthew 			mtx_enter(&sc->sc_port_mtx);
21133759911cSjmatthew 			qle_clear_port_lists(sc);
21143759911cSjmatthew 			TAILQ_INIT(&detach);
2115bab068e9Sbket 			TAILQ_CONCAT(&detach, &sc->sc_ports, ports);
21163759911cSjmatthew 			mtx_leave(&sc->sc_port_mtx);
21173759911cSjmatthew 
21183759911cSjmatthew 			while (!TAILQ_EMPTY(&detach)) {
21193759911cSjmatthew 				port = TAILQ_FIRST(&detach);
21203759911cSjmatthew 				TAILQ_REMOVE(&detach, port, ports);
21213759911cSjmatthew 				if (port->flags & QLE_PORT_FLAG_IS_TARGET) {
21223759911cSjmatthew 					scsi_detach_target(sc->sc_scsibus,
2123cc5e21deSkrw 					    port->loopid, DETACH_FORCE |
2124cc5e21deSkrw 					    DETACH_QUIET);
21253759911cSjmatthew 					sc->sc_targets[port->loopid] = NULL;
21263759911cSjmatthew 				}
21273759911cSjmatthew 				if (port->location & QLE_LOCATION_FABRIC)
21283759911cSjmatthew 					qle_fabric_plogo(sc, port);
21293759911cSjmatthew 
2130c9d76b5bSderaadt 				free(port, M_DEVBUF, sizeof *port);
21313759911cSjmatthew 			}
21323759911cSjmatthew 
21333759911cSjmatthew 			qle_update_done(sc, QLE_UPDATE_TASK_CLEAR_ALL);
21343759911cSjmatthew 			continue;
21353759911cSjmatthew 		}
21363759911cSjmatthew 
21373759911cSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_SOFTRESET) {
2138cc014dc0Sjmatthew 			DPRINTF(QLE_D_IO, "%s: attempting softreset\n",
2139cc014dc0Sjmatthew 			    DEVNAME(sc));
21403759911cSjmatthew 			if (qle_softreset(sc) != 0) {
2141cc014dc0Sjmatthew 				DPRINTF(QLE_D_IO, "%s: couldn't softreset\n",
2142cc014dc0Sjmatthew 				    DEVNAME(sc));
21433759911cSjmatthew 			}
21443759911cSjmatthew 			qle_update_done(sc, QLE_UPDATE_TASK_SOFTRESET);
21453759911cSjmatthew 			continue;
21463759911cSjmatthew 		}
21473759911cSjmatthew 
2148580dbe8fSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_UPDATE_TOPO) {
2149580dbe8fSjmatthew 			DPRINTF(QLE_D_PORT, "%s: updating topology\n",
2150580dbe8fSjmatthew 			    DEVNAME(sc));
2151580dbe8fSjmatthew 			qle_update_topology(sc);
2152580dbe8fSjmatthew 			qle_update_done(sc, QLE_UPDATE_TASK_UPDATE_TOPO);
2153580dbe8fSjmatthew 			continue;
2154580dbe8fSjmatthew 		}
2155580dbe8fSjmatthew 
2156580dbe8fSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_GET_PORT_LIST) {
2157580dbe8fSjmatthew 			DPRINTF(QLE_D_PORT, "%s: getting port name list\n",
2158580dbe8fSjmatthew 			    DEVNAME(sc));
2159580dbe8fSjmatthew 			mtx_enter(&sc->sc_port_mtx);
2160580dbe8fSjmatthew 			qle_clear_port_lists(sc);
2161580dbe8fSjmatthew 			mtx_leave(&sc->sc_port_mtx);
2162580dbe8fSjmatthew 
2163580dbe8fSjmatthew 			qle_get_port_name_list(sc, QLE_LOCATION_LOOP |
2164580dbe8fSjmatthew 			    QLE_LOCATION_FABRIC);
2165580dbe8fSjmatthew 			mtx_enter(&sc->sc_port_mtx);
2166580dbe8fSjmatthew 			TAILQ_FOREACH(port, &sc->sc_ports, ports) {
2167580dbe8fSjmatthew 				TAILQ_INSERT_TAIL(&sc->sc_ports_gone, port,
2168580dbe8fSjmatthew 				    update);
2169580dbe8fSjmatthew 				if (port->location & QLE_LOCATION_FABRIC) {
2170580dbe8fSjmatthew 					port->flags |=
2171580dbe8fSjmatthew 					    QLE_PORT_FLAG_NEEDS_LOGIN;
2172580dbe8fSjmatthew 				}
2173580dbe8fSjmatthew 			}
2174580dbe8fSjmatthew 
2175580dbe8fSjmatthew 			/* take care of ports that haven't changed first */
2176580dbe8fSjmatthew 			TAILQ_FOREACH(fport, &sc->sc_ports_found, update) {
2177580dbe8fSjmatthew 				port = sc->sc_targets[fport->loopid];
2178580dbe8fSjmatthew 				if (port == NULL || fport->port_name !=
2179580dbe8fSjmatthew 				    port->port_name) {
2180580dbe8fSjmatthew 					/* new or changed port, handled later */
2181580dbe8fSjmatthew 					continue;
2182580dbe8fSjmatthew 				}
2183580dbe8fSjmatthew 
2184580dbe8fSjmatthew 				/*
2185580dbe8fSjmatthew 				 * the port hasn't been logged out, which
2186580dbe8fSjmatthew 				 * means we don't need to log in again, and,
2187580dbe8fSjmatthew 				 * for loop ports, that the port still exists
2188580dbe8fSjmatthew 				 */
2189580dbe8fSjmatthew 				port->flags &= ~QLE_PORT_FLAG_NEEDS_LOGIN;
2190580dbe8fSjmatthew 				if (port->location & QLE_LOCATION_LOOP)
2191580dbe8fSjmatthew 					TAILQ_REMOVE(&sc->sc_ports_gone,
2192580dbe8fSjmatthew 					    port, update);
2193580dbe8fSjmatthew 
2194580dbe8fSjmatthew 				fport->location = 0;
2195580dbe8fSjmatthew 			}
2196580dbe8fSjmatthew 			mtx_leave(&sc->sc_port_mtx);
2197580dbe8fSjmatthew 			qle_update_start(sc, QLE_UPDATE_TASK_PORT_LIST);
2198580dbe8fSjmatthew 			qle_update_done(sc, QLE_UPDATE_TASK_GET_PORT_LIST);
2199580dbe8fSjmatthew 			continue;
2200580dbe8fSjmatthew 		}
2201580dbe8fSjmatthew 
2202580dbe8fSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_PORT_LIST) {
2203580dbe8fSjmatthew 			mtx_enter(&sc->sc_port_mtx);
2204580dbe8fSjmatthew 			fport = TAILQ_FIRST(&sc->sc_ports_found);
2205580dbe8fSjmatthew 			if (fport != NULL) {
2206580dbe8fSjmatthew 				TAILQ_REMOVE(&sc->sc_ports_found, fport,
2207580dbe8fSjmatthew 				    update);
2208580dbe8fSjmatthew 			}
2209580dbe8fSjmatthew 			mtx_leave(&sc->sc_port_mtx);
2210580dbe8fSjmatthew 
2211580dbe8fSjmatthew 			if (fport == NULL) {
2212580dbe8fSjmatthew 				DPRINTF(QLE_D_PORT, "%s: done with ports\n",
2213580dbe8fSjmatthew 				    DEVNAME(sc));
2214580dbe8fSjmatthew 				qle_update_done(sc,
2215580dbe8fSjmatthew 				    QLE_UPDATE_TASK_PORT_LIST);
2216580dbe8fSjmatthew 				qle_update_start(sc,
2217be68bd62Sjmatthew 				    QLE_UPDATE_TASK_SCAN_FABRIC);
2218580dbe8fSjmatthew 			} else if (fport->location & QLE_LOCATION_LOOP) {
2219580dbe8fSjmatthew 				DPRINTF(QLE_D_PORT, "%s: loop port %04x\n",
2220580dbe8fSjmatthew 				    DEVNAME(sc), fport->loopid);
2221580dbe8fSjmatthew 				if (qle_add_loop_port(sc, fport) != 0)
2222c9d76b5bSderaadt 					free(fport, M_DEVBUF, sizeof *port);
2223580dbe8fSjmatthew 			} else if (fport->location & QLE_LOCATION_FABRIC) {
2224580dbe8fSjmatthew 				qle_add_fabric_port(sc, fport);
2225580dbe8fSjmatthew 			} else {
2226580dbe8fSjmatthew 				/* already processed */
2227c9d76b5bSderaadt 				free(fport, M_DEVBUF, sizeof *port);
2228580dbe8fSjmatthew 			}
2229580dbe8fSjmatthew 			continue;
2230580dbe8fSjmatthew 		}
2231580dbe8fSjmatthew 
2232580dbe8fSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_SCAN_FABRIC) {
2233580dbe8fSjmatthew 			DPRINTF(QLE_D_PORT, "%s: starting fabric scan\n",
2234580dbe8fSjmatthew 			    DEVNAME(sc));
2235580dbe8fSjmatthew 			lastport = sc->sc_port_id;
2236580dbe8fSjmatthew 			firstport = 0xffffffff;
2237580dbe8fSjmatthew 			if (qle_update_fabric(sc))
2238580dbe8fSjmatthew 				qle_update_start(sc,
2239580dbe8fSjmatthew 				    QLE_UPDATE_TASK_SCANNING_FABRIC);
2240107785f0Sjmatthew 			else
2241107785f0Sjmatthew 				qle_update_start(sc,
2242107785f0Sjmatthew 				    QLE_UPDATE_TASK_ATTACH_TARGET |
2243107785f0Sjmatthew 				    QLE_UPDATE_TASK_DETACH_TARGET);
2244580dbe8fSjmatthew 
2245580dbe8fSjmatthew 			qle_update_done(sc, QLE_UPDATE_TASK_SCAN_FABRIC);
2246580dbe8fSjmatthew 			continue;
2247580dbe8fSjmatthew 		}
2248580dbe8fSjmatthew 
2249580dbe8fSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_SCANNING_FABRIC) {
2250580dbe8fSjmatthew 			fport = qle_next_fabric_port(sc, &firstport, &lastport);
2251580dbe8fSjmatthew 			if (fport != NULL) {
2252580dbe8fSjmatthew 				int disp;
2253580dbe8fSjmatthew 
2254580dbe8fSjmatthew 				mtx_enter(&sc->sc_port_mtx);
2255580dbe8fSjmatthew 				disp = qle_classify_port(sc, fport->location,
2256580dbe8fSjmatthew 				    fport->port_name, fport->node_name, &port);
2257580dbe8fSjmatthew 				switch (disp) {
2258580dbe8fSjmatthew 				case QLE_PORT_DISP_CHANGED:
2259580dbe8fSjmatthew 				case QLE_PORT_DISP_MOVED:
2260580dbe8fSjmatthew 					/* we'll log out the old port later */
2261580dbe8fSjmatthew 				case QLE_PORT_DISP_NEW:
2262580dbe8fSjmatthew 					DPRINTF(QLE_D_PORT, "%s: new port "
2263580dbe8fSjmatthew 					    "%06x\n", DEVNAME(sc),
2264580dbe8fSjmatthew 					    fport->portid);
2265580dbe8fSjmatthew 					TAILQ_INSERT_TAIL(&sc->sc_ports_found,
2266580dbe8fSjmatthew 					    fport, update);
2267580dbe8fSjmatthew 					break;
2268580dbe8fSjmatthew 				case QLE_PORT_DISP_DUP:
2269c9d76b5bSderaadt 					free(fport, M_DEVBUF, sizeof *port);
2270580dbe8fSjmatthew 					break;
2271580dbe8fSjmatthew 				case QLE_PORT_DISP_SAME:
2272580dbe8fSjmatthew 					DPRINTF(QLE_D_PORT, "%s: existing port "
2273580dbe8fSjmatthew 					    " %06x\n", DEVNAME(sc),
2274580dbe8fSjmatthew 					    fport->portid);
2275580dbe8fSjmatthew 					TAILQ_REMOVE(&sc->sc_ports_gone, port,
2276580dbe8fSjmatthew 					    update);
2277c9d76b5bSderaadt 					free(fport, M_DEVBUF, sizeof *port);
2278580dbe8fSjmatthew 					break;
2279580dbe8fSjmatthew 				}
2280580dbe8fSjmatthew 				mtx_leave(&sc->sc_port_mtx);
2281580dbe8fSjmatthew 			}
2282580dbe8fSjmatthew 			if (lastport == 0xffffffff) {
2283580dbe8fSjmatthew 				DPRINTF(QLE_D_PORT, "%s: finished\n",
2284580dbe8fSjmatthew 				    DEVNAME(sc));
2285580dbe8fSjmatthew 				qle_update_done(sc,
2286580dbe8fSjmatthew 				    QLE_UPDATE_TASK_SCANNING_FABRIC);
2287580dbe8fSjmatthew 				qle_update_start(sc,
2288580dbe8fSjmatthew 				    QLE_UPDATE_TASK_FABRIC_LOGIN);
2289580dbe8fSjmatthew 			}
2290580dbe8fSjmatthew 			continue;
2291580dbe8fSjmatthew 		}
2292580dbe8fSjmatthew 
2293580dbe8fSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_FABRIC_LOGIN) {
2294580dbe8fSjmatthew 			mtx_enter(&sc->sc_port_mtx);
2295580dbe8fSjmatthew 			port = TAILQ_FIRST(&sc->sc_ports_found);
2296580dbe8fSjmatthew 			if (port != NULL) {
2297580dbe8fSjmatthew 				TAILQ_REMOVE(&sc->sc_ports_found, port, update);
2298580dbe8fSjmatthew 			}
2299580dbe8fSjmatthew 			mtx_leave(&sc->sc_port_mtx);
2300580dbe8fSjmatthew 
2301580dbe8fSjmatthew 			if (port != NULL) {
2302580dbe8fSjmatthew 				DPRINTF(QLE_D_PORT, "%s: found port %06x\n",
2303580dbe8fSjmatthew 				    DEVNAME(sc), port->portid);
2304580dbe8fSjmatthew 				if (qle_fabric_plogi(sc, port) == 0) {
2305580dbe8fSjmatthew 					qle_add_fabric_port(sc, port);
2306580dbe8fSjmatthew 				} else {
2307580dbe8fSjmatthew 					DPRINTF(QLE_D_PORT, "%s: plogi %06x "
2308580dbe8fSjmatthew 					    "failed\n", DEVNAME(sc),
2309580dbe8fSjmatthew 					    port->portid);
2310c9d76b5bSderaadt 					free(port, M_DEVBUF, sizeof *port);
2311580dbe8fSjmatthew 				}
2312580dbe8fSjmatthew 			} else {
2313580dbe8fSjmatthew 				DPRINTF(QLE_D_PORT, "%s: done with logins\n",
2314580dbe8fSjmatthew 				    DEVNAME(sc));
2315580dbe8fSjmatthew 				qle_update_done(sc,
2316580dbe8fSjmatthew 				    QLE_UPDATE_TASK_FABRIC_LOGIN);
2317580dbe8fSjmatthew 				qle_update_start(sc,
2318580dbe8fSjmatthew 				    QLE_UPDATE_TASK_ATTACH_TARGET |
2319580dbe8fSjmatthew 				    QLE_UPDATE_TASK_DETACH_TARGET);
2320580dbe8fSjmatthew 			}
2321580dbe8fSjmatthew 			continue;
2322580dbe8fSjmatthew 		}
2323580dbe8fSjmatthew 
2324580dbe8fSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_FABRIC_RELOGIN) {
2325580dbe8fSjmatthew 			TAILQ_FOREACH(port, &sc->sc_ports, ports) {
2326580dbe8fSjmatthew 				if (port->flags & QLE_PORT_FLAG_NEEDS_LOGIN) {
2327580dbe8fSjmatthew 					qle_fabric_plogi(sc, port);
2328580dbe8fSjmatthew 					break;
2329580dbe8fSjmatthew 				}
2330580dbe8fSjmatthew 			}
2331580dbe8fSjmatthew 
2332abcbcc4dSdoug 			if (port == NULL)
2333580dbe8fSjmatthew 				qle_update_done(sc,
2334580dbe8fSjmatthew 				    QLE_UPDATE_TASK_FABRIC_RELOGIN);
2335580dbe8fSjmatthew 			continue;
2336580dbe8fSjmatthew 		}
2337580dbe8fSjmatthew 
23383759911cSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_DETACH_TARGET) {
23393759911cSjmatthew 			mtx_enter(&sc->sc_port_mtx);
23403759911cSjmatthew 			port = TAILQ_FIRST(&sc->sc_ports_gone);
23413759911cSjmatthew 			if (port != NULL) {
23423759911cSjmatthew 				sc->sc_targets[port->loopid] = NULL;
23433759911cSjmatthew 				TAILQ_REMOVE(&sc->sc_ports_gone, port, update);
23443759911cSjmatthew 				TAILQ_REMOVE(&sc->sc_ports, port, ports);
23453759911cSjmatthew 			}
23463759911cSjmatthew 			mtx_leave(&sc->sc_port_mtx);
23473759911cSjmatthew 
23483759911cSjmatthew 			if (port != NULL) {
2349cc014dc0Sjmatthew 				DPRINTF(QLE_D_PORT, "%s: detaching port %06x\n",
2350cc014dc0Sjmatthew 				    DEVNAME(sc), port->portid);
23513759911cSjmatthew 				if (sc->sc_scsibus != NULL)
23523759911cSjmatthew 					scsi_detach_target(sc->sc_scsibus,
2353cc5e21deSkrw 					    port->loopid, DETACH_FORCE |
2354cc5e21deSkrw 					    DETACH_QUIET);
23553759911cSjmatthew 
23563759911cSjmatthew 				if (port->location & QLE_LOCATION_FABRIC)
23573759911cSjmatthew 					qle_fabric_plogo(sc, port);
23583759911cSjmatthew 
2359c9d76b5bSderaadt 				free(port, M_DEVBUF, sizeof *port);
23603759911cSjmatthew 			} else {
2361cc014dc0Sjmatthew 				DPRINTF(QLE_D_PORT, "%s: nothing to detach\n",
2362cc014dc0Sjmatthew 				    DEVNAME(sc));
23633759911cSjmatthew 				qle_update_done(sc,
23643759911cSjmatthew 				    QLE_UPDATE_TASK_DETACH_TARGET);
23653759911cSjmatthew 			}
23663759911cSjmatthew 			continue;
23673759911cSjmatthew 		}
23683759911cSjmatthew 
23693759911cSjmatthew 		if (sc->sc_update_tasks & QLE_UPDATE_TASK_ATTACH_TARGET) {
23703759911cSjmatthew 			mtx_enter(&sc->sc_port_mtx);
23713759911cSjmatthew 			port = TAILQ_FIRST(&sc->sc_ports_new);
23723759911cSjmatthew 			if (port != NULL) {
23733759911cSjmatthew 				TAILQ_REMOVE(&sc->sc_ports_new, port, update);
23743759911cSjmatthew 				TAILQ_INSERT_TAIL(&sc->sc_ports, port, ports);
23753759911cSjmatthew 			}
23763759911cSjmatthew 			mtx_leave(&sc->sc_port_mtx);
23773759911cSjmatthew 
23783759911cSjmatthew 			if (port != NULL) {
23793759911cSjmatthew 				if (sc->sc_scsibus != NULL)
23803759911cSjmatthew 					scsi_probe_target(sc->sc_scsibus,
23813759911cSjmatthew 					    port->loopid);
23823759911cSjmatthew 			} else {
23833759911cSjmatthew 				qle_update_done(sc,
23843759911cSjmatthew 				    QLE_UPDATE_TASK_ATTACH_TARGET);
23853759911cSjmatthew 			}
23863759911cSjmatthew 			continue;
23873759911cSjmatthew 		}
23883759911cSjmatthew 
23893759911cSjmatthew 	}
23903759911cSjmatthew 
2391cc014dc0Sjmatthew 	DPRINTF(QLE_D_PORT, "%s: done updating\n", DEVNAME(sc));
23923759911cSjmatthew }
23933759911cSjmatthew 
23943759911cSjmatthew int
23953759911cSjmatthew qle_async(struct qle_softc *sc, u_int16_t info)
23963759911cSjmatthew {
23973759911cSjmatthew 	switch (info) {
23983759911cSjmatthew 	case QLE_ASYNC_SYSTEM_ERROR:
23993759911cSjmatthew 		qle_update_start(sc, QLE_UPDATE_TASK_SOFTRESET);
24003759911cSjmatthew 		break;
24013759911cSjmatthew 
24023759911cSjmatthew 	case QLE_ASYNC_REQ_XFER_ERROR:
24033759911cSjmatthew 		qle_update_start(sc, QLE_UPDATE_TASK_SOFTRESET);
24043759911cSjmatthew 		break;
24053759911cSjmatthew 
24063759911cSjmatthew 	case QLE_ASYNC_RSP_XFER_ERROR:
24073759911cSjmatthew 		qle_update_start(sc, QLE_UPDATE_TASK_SOFTRESET);
24083759911cSjmatthew 		break;
24093759911cSjmatthew 
24103759911cSjmatthew 	case QLE_ASYNC_LIP_OCCURRED:
2411cc014dc0Sjmatthew 		DPRINTF(QLE_D_INTR, "%s: lip occurred\n", DEVNAME(sc));
24123759911cSjmatthew 		break;
24133759911cSjmatthew 
24143759911cSjmatthew 	case QLE_ASYNC_LOOP_UP:
2415cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: loop up\n", DEVNAME(sc));
24163759911cSjmatthew 		sc->sc_loop_up = 1;
24173759911cSjmatthew 		sc->sc_marker_required = 1;
24183fc6fae1Sjmatthew 		qle_update_defer(sc, QLE_UPDATE_TASK_UPDATE_TOPO |
2419be68bd62Sjmatthew 		    QLE_UPDATE_TASK_GET_PORT_LIST);
24203759911cSjmatthew 		break;
24213759911cSjmatthew 
24223759911cSjmatthew 	case QLE_ASYNC_LOOP_DOWN:
2423cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: loop down\n", DEVNAME(sc));
24243759911cSjmatthew 		sc->sc_loop_up = 0;
24253fc6fae1Sjmatthew 		qle_update_cancel(sc);
24263759911cSjmatthew 		qle_update_start(sc, QLE_UPDATE_TASK_CLEAR_ALL);
24273759911cSjmatthew 		break;
24283759911cSjmatthew 
24293759911cSjmatthew 	case QLE_ASYNC_LIP_RESET:
2430cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: lip reset\n", DEVNAME(sc));
24313759911cSjmatthew 		sc->sc_marker_required = 1;
24323fc6fae1Sjmatthew 		qle_update_defer(sc, QLE_UPDATE_TASK_FABRIC_RELOGIN);
24333759911cSjmatthew 		break;
24343759911cSjmatthew 
24353759911cSjmatthew 	case QLE_ASYNC_PORT_DB_CHANGE:
2436cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: port db changed %x\n", DEVNAME(sc),
24373759911cSjmatthew 		    qle_read_mbox(sc, 1));
2438be68bd62Sjmatthew 		qle_update_start(sc, QLE_UPDATE_TASK_GET_PORT_LIST);
24393759911cSjmatthew 		break;
24403759911cSjmatthew 
24413759911cSjmatthew 	case QLE_ASYNC_CHANGE_NOTIFY:
2442cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: name server change (%02x:%02x)\n",
2443cc014dc0Sjmatthew 		    DEVNAME(sc), qle_read_mbox(sc, 1), qle_read_mbox(sc, 2));
2444be68bd62Sjmatthew 		qle_update_start(sc, QLE_UPDATE_TASK_GET_PORT_LIST);
24453759911cSjmatthew 		break;
24463759911cSjmatthew 
24473759911cSjmatthew 	case QLE_ASYNC_LIP_F8:
2448cc014dc0Sjmatthew 		DPRINTF(QLE_D_INTR, "%s: lip f8\n", DEVNAME(sc));
24493759911cSjmatthew 		break;
24503759911cSjmatthew 
24513759911cSjmatthew 	case QLE_ASYNC_LOOP_INIT_ERROR:
2452cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: loop initialization error: %x\n",
2453cc014dc0Sjmatthew 		    DEVNAME(sc), qle_read_mbox(sc, 1));
24543759911cSjmatthew 		break;
24553759911cSjmatthew 
24563759911cSjmatthew 	case QLE_ASYNC_POINT_TO_POINT:
2457cc014dc0Sjmatthew 		DPRINTF(QLE_D_PORT, "%s: connected in point-to-point mode\n",
2458cc014dc0Sjmatthew 		    DEVNAME(sc));
24593759911cSjmatthew 		break;
24603759911cSjmatthew 
24613759911cSjmatthew 	case QLE_ASYNC_ZIO_RESP_UPDATE:
24623759911cSjmatthew 		/* shouldn't happen, we don't do zio */
24633759911cSjmatthew 		break;
24643759911cSjmatthew 
24653759911cSjmatthew 	default:
2466cc014dc0Sjmatthew 		DPRINTF(QLE_D_INTR, "%s: unknown async %x\n", DEVNAME(sc), info);
24673759911cSjmatthew 		break;
24683759911cSjmatthew 	}
24693759911cSjmatthew 	return (1);
24703759911cSjmatthew }
24713759911cSjmatthew 
24723759911cSjmatthew void
24733759911cSjmatthew qle_dump_stuff(struct qle_softc *sc, void *buf, int n)
24743759911cSjmatthew {
2475cc014dc0Sjmatthew #ifdef QLE_DEBUG
24763759911cSjmatthew 	u_int8_t *d = buf;
24773759911cSjmatthew 	int l;
24783759911cSjmatthew 
2479cc014dc0Sjmatthew 	if ((qledebug & QLE_D_IOCB) == 0)
2480cc014dc0Sjmatthew 		return;
2481cc014dc0Sjmatthew 
24823759911cSjmatthew 	printf("%s: stuff\n", DEVNAME(sc));
24833759911cSjmatthew 	for (l = 0; l < n; l++) {
24843759911cSjmatthew 		printf(" %2.2x", d[l]);
24853759911cSjmatthew 		if (l % 16 == 15)
24863759911cSjmatthew 			printf("\n");
24873759911cSjmatthew 	}
24883759911cSjmatthew 	if (n % 16 != 0)
24893759911cSjmatthew 		printf("\n");
2490cc014dc0Sjmatthew #endif
24913759911cSjmatthew }
24923759911cSjmatthew 
24933759911cSjmatthew void
24943759911cSjmatthew qle_dump_iocb(struct qle_softc *sc, void *buf)
24953759911cSjmatthew {
2496cc014dc0Sjmatthew #ifdef QLE_DEBUG
24973759911cSjmatthew 	u_int8_t *iocb = buf;
24983759911cSjmatthew 	int l;
24993759911cSjmatthew 	int b;
25003759911cSjmatthew 
2501cc014dc0Sjmatthew 	if ((qledebug & QLE_D_IOCB) == 0)
2502cc014dc0Sjmatthew 		return;
2503cc014dc0Sjmatthew 
25043759911cSjmatthew 	printf("%s: iocb:\n", DEVNAME(sc));
25053759911cSjmatthew 	for (l = 0; l < 4; l++) {
25063759911cSjmatthew 		for (b = 0; b < 16; b++) {
25073759911cSjmatthew 			printf(" %2.2x", iocb[(l*16)+b]);
25083759911cSjmatthew 		}
25093759911cSjmatthew 		printf("\n");
25103759911cSjmatthew 	}
2511cc014dc0Sjmatthew #endif
25123759911cSjmatthew }
25133759911cSjmatthew 
25143759911cSjmatthew void
25153759911cSjmatthew qle_dump_iocb_segs(struct qle_softc *sc, void *segs, int n)
25163759911cSjmatthew {
2517cc014dc0Sjmatthew #ifdef QLE_DEBUG
25183759911cSjmatthew 	u_int8_t *buf = segs;
25193759911cSjmatthew 	int s, b;
2520cc014dc0Sjmatthew 
2521cc014dc0Sjmatthew 	if ((qledebug & QLE_D_IOCB) == 0)
2522cc014dc0Sjmatthew 		return;
2523cc014dc0Sjmatthew 
25243759911cSjmatthew 	printf("%s: iocb segs:\n", DEVNAME(sc));
25253759911cSjmatthew 	for (s = 0; s < n; s++) {
25263759911cSjmatthew 		for (b = 0; b < sizeof(struct qle_iocb_seg); b++) {
25273759911cSjmatthew 			printf(" %2.2x", buf[(s*(sizeof(struct qle_iocb_seg)))
25283759911cSjmatthew 			    + b]);
25293759911cSjmatthew 		}
25303759911cSjmatthew 		printf("\n");
25313759911cSjmatthew 	}
2532cc014dc0Sjmatthew #endif
25333759911cSjmatthew }
25343759911cSjmatthew 
25353759911cSjmatthew void
25363759911cSjmatthew qle_put_marker(struct qle_softc *sc, void *buf)
25373759911cSjmatthew {
25383759911cSjmatthew 	struct qle_iocb_marker *marker = buf;
25393759911cSjmatthew 
25403759911cSjmatthew 	marker->entry_type = QLE_IOCB_MARKER;
25413759911cSjmatthew 	marker->entry_count = 1;
25423759911cSjmatthew 	marker->seqno = 0;
25433759911cSjmatthew 	marker->flags = 0;
25443759911cSjmatthew 
25453759911cSjmatthew 	/* could be more specific here; isp(4) isn't */
25463759911cSjmatthew 	marker->target = 0;
25473759911cSjmatthew 	marker->modifier = QLE_IOCB_MARKER_SYNC_ALL;
25483759911cSjmatthew }
25493759911cSjmatthew 
25503759911cSjmatthew void
2551a0abcb68Sdlg qle_sge(struct qle_iocb_seg *seg, u_int64_t addr, u_int32_t len)
25523759911cSjmatthew {
2553a0abcb68Sdlg 	htolem32(&seg->seg_addr_lo, addr);
2554a0abcb68Sdlg 	htolem32(&seg->seg_addr_hi, addr >> 32);
2555a0abcb68Sdlg 	htolem32(&seg->seg_len, len);
25563759911cSjmatthew }
25573759911cSjmatthew 
25583759911cSjmatthew void
25593759911cSjmatthew qle_put_cmd(struct qle_softc *sc, void *buf, struct scsi_xfer *xs,
25603759911cSjmatthew     struct qle_ccb *ccb, u_int32_t target_port)
25613759911cSjmatthew {
2562a0abcb68Sdlg 	bus_dmamap_t dmap = ccb->ccb_dmamap;
25633759911cSjmatthew 	struct qle_iocb_req6 *req = buf;
25643759911cSjmatthew 	struct qle_fcp_cmnd *cmnd;
25653759911cSjmatthew 	u_int64_t fcp_cmnd_offset;
25663759911cSjmatthew 	u_int32_t fcp_dl;
25673759911cSjmatthew 	int seg;
25683759911cSjmatthew 	int target = xs->sc_link->target;
25693759911cSjmatthew 	int lun = xs->sc_link->lun;
2570f1b43acaSdlg 	u_int16_t flags;
25713759911cSjmatthew 
25723759911cSjmatthew 	memset(req, 0, sizeof(*req));
25733759911cSjmatthew 	req->entry_type = QLE_IOCB_CMD_TYPE_6;
25743759911cSjmatthew 	req->entry_count = 1;
25753759911cSjmatthew 
25763759911cSjmatthew 	req->req_handle = ccb->ccb_id;
2577f1b43acaSdlg 	htolem16(&req->req_nport_handle, target);
25783759911cSjmatthew 
25793759911cSjmatthew 	/*
25803759911cSjmatthew 	 * timeout is in seconds.  make sure it's at least 1 if a timeout
25813759911cSjmatthew 	 * was specified in xs
25823759911cSjmatthew 	 */
25833759911cSjmatthew 	if (xs->timeout != 0)
2584f1b43acaSdlg 		htolem16(&req->req_timeout, MAX(1, xs->timeout/1000));
25853759911cSjmatthew 
25863759911cSjmatthew 	if (xs->datalen > 0) {
2587f1b43acaSdlg 		flags = (xs->flags & SCSI_DATA_IN) ?
2588f1b43acaSdlg 		    QLE_IOCB_CTRL_FLAG_READ : QLE_IOCB_CTRL_FLAG_WRITE;
2589a0abcb68Sdlg 		if (dmap->dm_nsegs == 1) {
2590a0abcb68Sdlg 			qle_sge(&req->req_data_seg, dmap->dm_segs[0].ds_addr,
2591a0abcb68Sdlg 			    dmap->dm_segs[0].ds_len);
25923759911cSjmatthew 		} else {
2593f1b43acaSdlg 			flags |= QLE_IOCB_CTRL_FLAG_EXT_SEG;
2594a0abcb68Sdlg 			for (seg = 0; seg < dmap->dm_nsegs; seg++) {
2595a0abcb68Sdlg 				qle_sge(&ccb->ccb_segs[seg],
2596a0abcb68Sdlg 				    dmap->dm_segs[seg].ds_addr,
2597a0abcb68Sdlg 				    dmap->dm_segs[seg].ds_len);
25983759911cSjmatthew 			}
259920c4379eSjmatthew 			qle_sge(&ccb->ccb_segs[seg++], 0, 0);
2600a0abcb68Sdlg 
26013759911cSjmatthew 			bus_dmamap_sync(sc->sc_dmat,
26023759911cSjmatthew 			    QLE_DMA_MAP(sc->sc_segments), ccb->ccb_seg_offset,
260320c4379eSjmatthew 			    seg * sizeof(*ccb->ccb_segs),
26043759911cSjmatthew 			    BUS_DMASYNC_PREWRITE);
260520c4379eSjmatthew 
260620c4379eSjmatthew 			qle_sge(&req->req_data_seg,
260720c4379eSjmatthew 			    QLE_DMA_DVA(sc->sc_segments) + ccb->ccb_seg_offset,
260820c4379eSjmatthew 			    seg * sizeof(struct qle_iocb_seg));
26093759911cSjmatthew 		}
26103759911cSjmatthew 
2611f1b43acaSdlg 		htolem16(&req->req_data_seg_count, dmap->dm_nsegs);
2612f1b43acaSdlg 		htolem32(&req->req_data_len, xs->datalen);
2613f1b43acaSdlg 		htolem16(&req->req_ctrl_flags, flags);
2614f1b43acaSdlg 	}
2615f1b43acaSdlg 
2616f1b43acaSdlg 	htobem16(&req->req_fcp_lun[0], lun);
2617f1b43acaSdlg 	htobem16(&req->req_fcp_lun[1], lun >> 16);
2618f1b43acaSdlg 	htolem32(&req->req_target_id, target_port & 0xffffff);
26193759911cSjmatthew 
26203759911cSjmatthew 	fcp_cmnd_offset = ccb->ccb_id * sizeof(*cmnd);
26213759911cSjmatthew 	/* set up FCP_CMND */
26223759911cSjmatthew 	cmnd = (struct qle_fcp_cmnd *)QLE_DMA_KVA(sc->sc_fcp_cmnds) +
26233759911cSjmatthew 	    ccb->ccb_id;
26243759911cSjmatthew 
26253759911cSjmatthew 	memset(cmnd, 0, sizeof(*cmnd));
2626f1b43acaSdlg 	htobem16(&cmnd->fcp_lun[0], lun);
2627f1b43acaSdlg 	htobem16(&cmnd->fcp_lun[1], lun >> 16);
26283759911cSjmatthew 	/* cmnd->fcp_task_attr = TSK_SIMPLE; */
26293759911cSjmatthew 	/* cmnd->fcp_task_mgmt = 0; */
2630664c6166Skrw 	memcpy(cmnd->fcp_cdb, &xs->cmd, xs->cmdlen);
26313759911cSjmatthew 
26323759911cSjmatthew 	/* FCP_DL goes after the cdb */
26333759911cSjmatthew 	fcp_dl = htobe32(xs->datalen);
26343759911cSjmatthew 	if (xs->cmdlen > 16) {
2635f1b43acaSdlg 		htolem16(&req->req_fcp_cmnd_len, 12 + xs->cmdlen + 4);
26363759911cSjmatthew 		cmnd->fcp_add_cdb_len = xs->cmdlen - 16;
2637bd6c3caaSdlg 		memcpy(cmnd->fcp_cdb + xs->cmdlen, &fcp_dl, sizeof(fcp_dl));
26383759911cSjmatthew 	} else {
2639f1b43acaSdlg 		htolem16(&req->req_fcp_cmnd_len, 12 + 16 + 4);
26403759911cSjmatthew 		cmnd->fcp_add_cdb_len = 0;
26413759911cSjmatthew 		memcpy(cmnd->fcp_cdb + 16, &fcp_dl, sizeof(fcp_dl));
26423759911cSjmatthew 	}
26433759911cSjmatthew 	if (xs->datalen > 0)
26443759911cSjmatthew 		cmnd->fcp_add_cdb_len |= (xs->flags & SCSI_DATA_IN) ? 2 : 1;
26453759911cSjmatthew 
26463759911cSjmatthew 	bus_dmamap_sync(sc->sc_dmat,
26473759911cSjmatthew 	    QLE_DMA_MAP(sc->sc_fcp_cmnds), fcp_cmnd_offset,
26483759911cSjmatthew 	    sizeof(*cmnd), BUS_DMASYNC_PREWRITE);
2649f1b43acaSdlg 
2650f1b43acaSdlg 	/* link req to cmnd */
2651f1b43acaSdlg 	fcp_cmnd_offset += QLE_DMA_DVA(sc->sc_fcp_cmnds);
2652f1b43acaSdlg 	htolem32(&req->req_fcp_cmnd_addr_lo, fcp_cmnd_offset);
2653f1b43acaSdlg 	htolem32(&req->req_fcp_cmnd_addr_hi, fcp_cmnd_offset >> 32);
26543759911cSjmatthew }
26553759911cSjmatthew 
26563759911cSjmatthew int
2657b803df0bSjmatthew qle_load_fwchunk(struct qle_softc *sc, struct qle_dmamem *mem,
26583759911cSjmatthew     const u_int32_t *src)
26593759911cSjmatthew {
26603759911cSjmatthew 	u_int32_t dest, done, total;
26613759911cSjmatthew 	int i;
26623759911cSjmatthew 
26633759911cSjmatthew 	dest = src[2];
26643759911cSjmatthew 	done = 0;
26653759911cSjmatthew 	total = src[3];
26663759911cSjmatthew 
26673759911cSjmatthew 	while (done < total) {
26683759911cSjmatthew 		u_int32_t *copy;
26693759911cSjmatthew 		u_int32_t words;
26703759911cSjmatthew 
26713759911cSjmatthew 		/* limit transfer size otherwise it just doesn't work */
26723759911cSjmatthew 		words = MIN(total - done, 1 << 10);
26733759911cSjmatthew 		copy = QLE_DMA_KVA(mem);
26743759911cSjmatthew 		for (i = 0; i < words; i++) {
267561ff084cSdlg 			htolem32(&copy[i], src[done++]);
26763759911cSjmatthew 		}
26773759911cSjmatthew 		bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0, words * 4,
26783759911cSjmatthew 		    BUS_DMASYNC_PREWRITE);
26793759911cSjmatthew 
26803759911cSjmatthew 		sc->sc_mbox[0] = QLE_MBOX_LOAD_RISC_RAM;
26813759911cSjmatthew 		sc->sc_mbox[1] = dest;
26823759911cSjmatthew 		sc->sc_mbox[4] = words >> 16;
26833759911cSjmatthew 		sc->sc_mbox[5] = words & 0xffff;
26843759911cSjmatthew 		sc->sc_mbox[8] = dest >> 16;
26853759911cSjmatthew 		qle_mbox_putaddr(sc->sc_mbox, mem);
268665fd2507Sjmatthew 		if (qle_mbox(sc, 0x01ff)) {
26873759911cSjmatthew 			printf("firmware load failed\n");
26883759911cSjmatthew 			return (1);
26893759911cSjmatthew 		}
26903759911cSjmatthew 		bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0, words * 4,
26913759911cSjmatthew 		    BUS_DMASYNC_POSTWRITE);
26923759911cSjmatthew 
26933759911cSjmatthew 		dest += words;
26943759911cSjmatthew 	}
26953759911cSjmatthew 
2696c8005c8bSjmatthew 	return (qle_verify_firmware(sc, src[2]));
26973759911cSjmatthew }
26983759911cSjmatthew 
26993759911cSjmatthew int
2700b803df0bSjmatthew qle_load_firmware_chunks(struct qle_softc *sc, const u_int32_t *fw)
27013759911cSjmatthew {
27023759911cSjmatthew 	struct qle_dmamem *mem;
2703c8005c8bSjmatthew 	int res = 0;
27043759911cSjmatthew 
27053759911cSjmatthew 	mem = qle_dmamem_alloc(sc, 65536);
27063759911cSjmatthew 	for (;;) {
2707c8005c8bSjmatthew 		if (qle_load_fwchunk(sc, mem, fw)) {
2708c8005c8bSjmatthew 			res = 1;
2709c8005c8bSjmatthew 			break;
2710c8005c8bSjmatthew 		}
27113759911cSjmatthew 		if (fw[1] == 0)
27123759911cSjmatthew 			break;
27133759911cSjmatthew 		fw += fw[3];
27143759911cSjmatthew 	}
27153759911cSjmatthew 
27163759911cSjmatthew 	qle_dmamem_free(sc, mem);
2717c8005c8bSjmatthew 	return (res);
2718c8005c8bSjmatthew }
2719c8005c8bSjmatthew 
2720c8005c8bSjmatthew u_int32_t
2721c8005c8bSjmatthew qle_read_ram_word(struct qle_softc *sc, u_int32_t addr)
2722c8005c8bSjmatthew {
2723c8005c8bSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_READ_RISC_RAM;
2724c8005c8bSjmatthew 	sc->sc_mbox[1] = addr & 0xffff;
2725c8005c8bSjmatthew 	sc->sc_mbox[8] = addr >> 16;
272665fd2507Sjmatthew 	if (qle_mbox(sc, 0x0103)) {
2727c8005c8bSjmatthew 		return (0);
2728c8005c8bSjmatthew 	}
2729c8005c8bSjmatthew 	return ((sc->sc_mbox[3] << 16) | sc->sc_mbox[2]);
2730c8005c8bSjmatthew }
2731c8005c8bSjmatthew 
2732c8005c8bSjmatthew int
2733c8005c8bSjmatthew qle_verify_firmware(struct qle_softc *sc, u_int32_t addr)
2734c8005c8bSjmatthew {
2735c8005c8bSjmatthew 	/*
2736c8005c8bSjmatthew 	 * QLE_MBOX_VERIFY_CSUM requires at least the firmware header
2737c8005c8bSjmatthew 	 * to be correct, otherwise it wanders all over ISP memory and
2738c8005c8bSjmatthew 	 * gets lost.  Check that chunk address (addr+2) is right and
2739c8005c8bSjmatthew 	 * size (addr+3) is plausible first.
2740c8005c8bSjmatthew 	 */
2741c8005c8bSjmatthew 	if ((qle_read_ram_word(sc, addr+2) != addr) ||
2742c8005c8bSjmatthew 	    (qle_read_ram_word(sc, addr+3) > 0xffff)) {
2743c8005c8bSjmatthew 		return (1);
2744c8005c8bSjmatthew 	}
2745c8005c8bSjmatthew 
2746c8005c8bSjmatthew 	sc->sc_mbox[0] = QLE_MBOX_VERIFY_CSUM;
2747c8005c8bSjmatthew 	sc->sc_mbox[1] = addr >> 16;
2748c8005c8bSjmatthew 	sc->sc_mbox[2] = addr;
274965fd2507Sjmatthew 	if (qle_mbox(sc, 0x0007)) {
2750c8005c8bSjmatthew 		return (1);
2751c8005c8bSjmatthew 	}
27523759911cSjmatthew 	return (0);
27533759911cSjmatthew }
27543759911cSjmatthew 
27553759911cSjmatthew int
27563759911cSjmatthew qle_read_nvram(struct qle_softc *sc)
27573759911cSjmatthew {
27583759911cSjmatthew 	u_int32_t data[sizeof(sc->sc_nvram) / 4];
27593759911cSjmatthew 	u_int32_t csum, tmp, v;
27603759911cSjmatthew 	int i, base, l;
27613759911cSjmatthew 
2762bea9c85cSjmatthew 	switch (sc->sc_isp_gen) {
2763bea9c85cSjmatthew 	case QLE_GEN_ISP24XX:
2764bea9c85cSjmatthew 		base = 0x7ffe0080;
2765bea9c85cSjmatthew 		break;
2766bea9c85cSjmatthew 	case QLE_GEN_ISP25XX:
2767bea9c85cSjmatthew 		base = 0x7ff48080;
2768bea9c85cSjmatthew 		break;
2769bea9c85cSjmatthew 	}
2770bea9c85cSjmatthew 	base += sc->sc_port * 0x100;
27713759911cSjmatthew 
27723759911cSjmatthew 	csum = 0;
27733759911cSjmatthew 	for (i = 0; i < nitems(data); i++) {
27743759911cSjmatthew 		data[i] = 0xffffffff;
2775bea9c85cSjmatthew 		qle_write(sc, QLE_FLASH_NVRAM_ADDR, base + i);
27763759911cSjmatthew 		for (l = 0; l < 5000; l++) {
27773759911cSjmatthew 			delay(10);
27783759911cSjmatthew 			tmp = qle_read(sc, QLE_FLASH_NVRAM_ADDR);
27793759911cSjmatthew 			if (tmp & (1U << 31)) {
27803759911cSjmatthew 				v = qle_read(sc, QLE_FLASH_NVRAM_DATA);
27813759911cSjmatthew 				csum += v;
27823759911cSjmatthew 				data[i] = letoh32(v);
27833759911cSjmatthew 				break;
27843759911cSjmatthew 			}
27853759911cSjmatthew 		}
27863759911cSjmatthew 	}
27873759911cSjmatthew 
27883759911cSjmatthew 	bcopy(data, &sc->sc_nvram, sizeof(sc->sc_nvram));
27893759911cSjmatthew 	/* id field should be 'ISP' */
27903759911cSjmatthew 	if (sc->sc_nvram.id[0] != 'I' || sc->sc_nvram.id[1] != 'S' ||
27913759911cSjmatthew 	    sc->sc_nvram.id[2] != 'P' || csum != 0) {
27923759911cSjmatthew 		printf("%s: nvram corrupt\n", DEVNAME(sc));
27933759911cSjmatthew 		return (1);
27943759911cSjmatthew 	}
27953759911cSjmatthew 	return (0);
27963759911cSjmatthew }
27973759911cSjmatthew 
27983759911cSjmatthew struct qle_dmamem *
27993759911cSjmatthew qle_dmamem_alloc(struct qle_softc *sc, size_t size)
28003759911cSjmatthew {
28013759911cSjmatthew 	struct qle_dmamem *m;
28023759911cSjmatthew 	int nsegs;
28033759911cSjmatthew 
28043759911cSjmatthew 	m = malloc(sizeof(*m), M_DEVBUF, M_NOWAIT | M_ZERO);
28053759911cSjmatthew 	if (m == NULL)
28063759911cSjmatthew 		return (NULL);
28073759911cSjmatthew 
28083759911cSjmatthew 	m->qdm_size = size;
28093759911cSjmatthew 
28103759911cSjmatthew 	if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
28113759911cSjmatthew 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &m->qdm_map) != 0)
28123759911cSjmatthew 		goto qdmfree;
28133759911cSjmatthew 
28143759911cSjmatthew 	if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &m->qdm_seg, 1,
28153759911cSjmatthew 	    &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0)
28163759911cSjmatthew 		goto destroy;
28173759911cSjmatthew 
28183759911cSjmatthew 	if (bus_dmamem_map(sc->sc_dmat, &m->qdm_seg, nsegs, size, &m->qdm_kva,
28193759911cSjmatthew 	    BUS_DMA_NOWAIT) != 0)
28203759911cSjmatthew 		goto free;
28213759911cSjmatthew 
28223759911cSjmatthew 	if (bus_dmamap_load(sc->sc_dmat, m->qdm_map, m->qdm_kva, size, NULL,
28233759911cSjmatthew 	    BUS_DMA_NOWAIT) != 0)
28243759911cSjmatthew 		goto unmap;
28253759911cSjmatthew 
28263759911cSjmatthew 	return (m);
28273759911cSjmatthew 
28283759911cSjmatthew unmap:
28293759911cSjmatthew 	bus_dmamem_unmap(sc->sc_dmat, m->qdm_kva, m->qdm_size);
28303759911cSjmatthew free:
28313759911cSjmatthew 	bus_dmamem_free(sc->sc_dmat, &m->qdm_seg, 1);
28323759911cSjmatthew destroy:
28333759911cSjmatthew 	bus_dmamap_destroy(sc->sc_dmat, m->qdm_map);
28343759911cSjmatthew qdmfree:
2835c9d76b5bSderaadt 	free(m, M_DEVBUF, sizeof *m);
28363759911cSjmatthew 
28373759911cSjmatthew 	return (NULL);
28383759911cSjmatthew }
28393759911cSjmatthew 
28403759911cSjmatthew void
28413759911cSjmatthew qle_dmamem_free(struct qle_softc *sc, struct qle_dmamem *m)
28423759911cSjmatthew {
28433759911cSjmatthew 	bus_dmamap_unload(sc->sc_dmat, m->qdm_map);
28443759911cSjmatthew 	bus_dmamem_unmap(sc->sc_dmat, m->qdm_kva, m->qdm_size);
28453759911cSjmatthew 	bus_dmamem_free(sc->sc_dmat, &m->qdm_seg, 1);
28463759911cSjmatthew 	bus_dmamap_destroy(sc->sc_dmat, m->qdm_map);
2847c9d76b5bSderaadt 	free(m, M_DEVBUF, sizeof *m);
28483759911cSjmatthew }
28493759911cSjmatthew 
28503759911cSjmatthew int
28513759911cSjmatthew qle_alloc_ccbs(struct qle_softc *sc)
28523759911cSjmatthew {
28533759911cSjmatthew 	struct qle_ccb		*ccb;
28543759911cSjmatthew 	u_int8_t		*cmd;
28553759911cSjmatthew 	int			i;
28563759911cSjmatthew 
28573759911cSjmatthew 	SIMPLEQ_INIT(&sc->sc_ccb_free);
28583759911cSjmatthew 	mtx_init(&sc->sc_ccb_mtx, IPL_BIO);
28593759911cSjmatthew 	mtx_init(&sc->sc_queue_mtx, IPL_BIO);
28603759911cSjmatthew 	mtx_init(&sc->sc_port_mtx, IPL_BIO);
28613759911cSjmatthew 	mtx_init(&sc->sc_mbox_mtx, IPL_BIO);
28623759911cSjmatthew 
28639f6fb5c7Sderaadt 	sc->sc_ccbs = mallocarray(sc->sc_maxcmds, sizeof(struct qle_ccb),
28643759911cSjmatthew 	    M_DEVBUF, M_WAITOK | M_CANFAIL | M_ZERO);
28653759911cSjmatthew 	if (sc->sc_ccbs == NULL) {
28663759911cSjmatthew 		printf("%s: unable to allocate ccbs\n", DEVNAME(sc));
28673759911cSjmatthew 		return (1);
28683759911cSjmatthew 	}
28693759911cSjmatthew 
28703759911cSjmatthew 	sc->sc_requests = qle_dmamem_alloc(sc, sc->sc_maxcmds *
28713759911cSjmatthew 	    QLE_QUEUE_ENTRY_SIZE);
28723759911cSjmatthew 	if (sc->sc_requests == NULL) {
28733759911cSjmatthew 		printf("%s: unable to allocate ccb dmamem\n", DEVNAME(sc));
28743759911cSjmatthew 		goto free_ccbs;
28753759911cSjmatthew 	}
28763759911cSjmatthew 	sc->sc_responses = qle_dmamem_alloc(sc, sc->sc_maxcmds *
28773759911cSjmatthew 	    QLE_QUEUE_ENTRY_SIZE);
28783759911cSjmatthew 	if (sc->sc_responses == NULL) {
28793759911cSjmatthew 		printf("%s: unable to allocate rcb dmamem\n", DEVNAME(sc));
28803759911cSjmatthew 		goto free_req;
28813759911cSjmatthew 	}
28823759911cSjmatthew 	sc->sc_pri_requests = qle_dmamem_alloc(sc, 8 * QLE_QUEUE_ENTRY_SIZE);
28833759911cSjmatthew 	if (sc->sc_pri_requests == NULL) {
28843759911cSjmatthew 		printf("%s: unable to allocate pri ccb dmamem\n", DEVNAME(sc));
288593cb4b70Sdaniel 		goto free_res;
28863759911cSjmatthew 	}
28873759911cSjmatthew 	sc->sc_segments = qle_dmamem_alloc(sc, sc->sc_maxcmds * QLE_MAX_SEGS *
28883759911cSjmatthew 	    sizeof(struct qle_iocb_seg));
28893759911cSjmatthew 	if (sc->sc_segments == NULL) {
28903759911cSjmatthew 		printf("%s: unable to allocate iocb segments\n", DEVNAME(sc));
289193cb4b70Sdaniel 		goto free_pri;
28923759911cSjmatthew 	}
28933759911cSjmatthew 
28943759911cSjmatthew 	sc->sc_fcp_cmnds = qle_dmamem_alloc(sc, sc->sc_maxcmds *
28953759911cSjmatthew 	    sizeof(struct qle_fcp_cmnd));
28963759911cSjmatthew 	if (sc->sc_fcp_cmnds == NULL) {
28973759911cSjmatthew 		printf("%s: unable to allocate FCP_CMNDs\n", DEVNAME(sc));
28983759911cSjmatthew 		goto free_seg;
28993759911cSjmatthew 	}
29003759911cSjmatthew 
29013759911cSjmatthew 	cmd = QLE_DMA_KVA(sc->sc_requests);
29023759911cSjmatthew 	memset(cmd, 0, QLE_QUEUE_ENTRY_SIZE * sc->sc_maxcmds);
29033759911cSjmatthew 	for (i = 0; i < sc->sc_maxcmds; i++) {
29043759911cSjmatthew 		ccb = &sc->sc_ccbs[i];
29053759911cSjmatthew 
29063759911cSjmatthew 		if (bus_dmamap_create(sc->sc_dmat, MAXPHYS,
290720c4379eSjmatthew 		    QLE_MAX_SEGS-1, MAXPHYS, 0,
29083759911cSjmatthew 		    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
29093759911cSjmatthew 		    &ccb->ccb_dmamap) != 0) {
29103759911cSjmatthew 			printf("%s: unable to create dma map\n", DEVNAME(sc));
29113759911cSjmatthew 			goto free_maps;
29123759911cSjmatthew 		}
29133759911cSjmatthew 
29143759911cSjmatthew 		ccb->ccb_sc = sc;
29153759911cSjmatthew 		ccb->ccb_id = i;
29163759911cSjmatthew 
29173759911cSjmatthew 		ccb->ccb_seg_offset = i * QLE_MAX_SEGS *
29183759911cSjmatthew 		    sizeof(struct qle_iocb_seg);
29193759911cSjmatthew 		ccb->ccb_segs = QLE_DMA_KVA(sc->sc_segments) +
29203759911cSjmatthew 		    ccb->ccb_seg_offset;
29213759911cSjmatthew 
29223759911cSjmatthew 		qle_put_ccb(sc, ccb);
29233759911cSjmatthew 	}
29243759911cSjmatthew 
29253759911cSjmatthew 	scsi_iopool_init(&sc->sc_iopool, sc, qle_get_ccb, qle_put_ccb);
29263759911cSjmatthew 	return (0);
29273759911cSjmatthew 
29283759911cSjmatthew free_maps:
29293759911cSjmatthew 	while ((ccb = qle_get_ccb(sc)) != NULL)
29303759911cSjmatthew 		bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
29313759911cSjmatthew 
29323759911cSjmatthew 	qle_dmamem_free(sc, sc->sc_fcp_cmnds);
29333759911cSjmatthew free_seg:
29343759911cSjmatthew 	qle_dmamem_free(sc, sc->sc_segments);
29353759911cSjmatthew free_pri:
29363759911cSjmatthew 	qle_dmamem_free(sc, sc->sc_pri_requests);
29373759911cSjmatthew free_res:
29383759911cSjmatthew 	qle_dmamem_free(sc, sc->sc_responses);
29393759911cSjmatthew free_req:
29403759911cSjmatthew 	qle_dmamem_free(sc, sc->sc_requests);
29413759911cSjmatthew free_ccbs:
2942df2ac69fStedu 	free(sc->sc_ccbs, M_DEVBUF, 0);
29433759911cSjmatthew 
29443759911cSjmatthew 	return (1);
29453759911cSjmatthew }
29463759911cSjmatthew 
29473759911cSjmatthew void
29483759911cSjmatthew qle_free_ccbs(struct qle_softc *sc)
29493759911cSjmatthew {
29503759911cSjmatthew 	struct qle_ccb		*ccb;
29513759911cSjmatthew 
29523759911cSjmatthew 	scsi_iopool_destroy(&sc->sc_iopool);
29533759911cSjmatthew 	while ((ccb = qle_get_ccb(sc)) != NULL)
29543759911cSjmatthew 		bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
29553759911cSjmatthew 	qle_dmamem_free(sc, sc->sc_segments);
29563759911cSjmatthew 	qle_dmamem_free(sc, sc->sc_responses);
29573759911cSjmatthew 	qle_dmamem_free(sc, sc->sc_requests);
2958df2ac69fStedu 	free(sc->sc_ccbs, M_DEVBUF, 0);
29593759911cSjmatthew }
29603759911cSjmatthew 
29613759911cSjmatthew void *
29623759911cSjmatthew qle_get_ccb(void *xsc)
29633759911cSjmatthew {
29643759911cSjmatthew 	struct qle_softc 	*sc = xsc;
29653759911cSjmatthew 	struct qle_ccb		*ccb;
29663759911cSjmatthew 
29673759911cSjmatthew 	mtx_enter(&sc->sc_ccb_mtx);
29683759911cSjmatthew 	ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free);
29693759911cSjmatthew 	if (ccb != NULL) {
29703759911cSjmatthew 		SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb_link);
29713759911cSjmatthew 	}
29723759911cSjmatthew 	mtx_leave(&sc->sc_ccb_mtx);
29733759911cSjmatthew 	return (ccb);
29743759911cSjmatthew }
29753759911cSjmatthew 
29763759911cSjmatthew void
29773759911cSjmatthew qle_put_ccb(void *xsc, void *io)
29783759911cSjmatthew {
29793759911cSjmatthew 	struct qle_softc	*sc = xsc;
29803759911cSjmatthew 	struct qle_ccb		*ccb = io;
29813759911cSjmatthew 
29823759911cSjmatthew 	ccb->ccb_xs = NULL;
29833759911cSjmatthew 	mtx_enter(&sc->sc_ccb_mtx);
29843759911cSjmatthew 	SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_link);
29853759911cSjmatthew 	mtx_leave(&sc->sc_ccb_mtx);
29863759911cSjmatthew }
2987