xref: /netbsd-src/sys/dev/pci/mlx_pci.c (revision 17dd36da8292193180754d5047c0926dbb56818c)
1 /*	$NetBSD: mlx_pci.c,v 1.2 2001/02/18 21:02:54 mjacob Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Doran.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*-
40  * Copyright (c) 1999 Michael Smith
41  * All rights reserved.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  *
64  * from FreeBSD: mlx_pci.c,v 1.4.2.4 2000/10/28 10:48:09 msmith Exp
65  */
66 
67 /*
68  * PCI front-end for the mlx(4) driver.
69  */
70 
71 #include <sys/param.h>
72 #include <sys/systm.h>
73 #include <sys/kernel.h>
74 #include <sys/device.h>
75 #include <sys/queue.h>
76 #include <sys/callout.h>
77 
78 #include <machine/endian.h>
79 #include <machine/bus.h>
80 
81 #include <dev/ic/mlxreg.h>
82 #include <dev/ic/mlxio.h>
83 #include <dev/ic/mlxvar.h>
84 
85 #include <dev/pci/pcireg.h>
86 #include <dev/pci/pcivar.h>
87 #include <dev/pci/pcidevs.h>
88 
89 static void	mlx_pci_attach(struct device *, struct device *, void *);
90 static int	mlx_pci_match(struct device *, struct cfdata *, void *);
91 static const struct mlx_pci_ident *mlx_pci_findmpi(struct pci_attach_args *);
92 
93 static int	mlx_v3_submit(struct mlx_softc *, struct mlx_ccb *);
94 static int	mlx_v3_findcomplete(struct mlx_softc *, u_int *, u_int *);
95 static void	mlx_v3_intaction(struct mlx_softc *, int);
96 static int	mlx_v3_fw_handshake(struct mlx_softc *, int *, int *, int *);
97 
98 static int	mlx_v4_submit(struct mlx_softc *, struct mlx_ccb *);
99 static int	mlx_v4_findcomplete(struct mlx_softc *, u_int *, u_int *);
100 static void	mlx_v4_intaction(struct mlx_softc *, int);
101 static int	mlx_v4_fw_handshake(struct mlx_softc *, int *, int *, int *);
102 
103 static int	mlx_v5_submit(struct mlx_softc *, struct mlx_ccb *);
104 static int	mlx_v5_findcomplete(struct mlx_softc *, u_int *, u_int *);
105 static void	mlx_v5_intaction(struct mlx_softc *, int);
106 static int	mlx_v5_fw_handshake(struct mlx_softc *, int *, int *, int *);
107 
108 struct mlx_pci_ident {
109 	u_short	mpi_vendor;
110 	u_short	mpi_product;
111 	u_short	mpi_subvendor;
112 	u_short	mpi_subproduct;
113 	int	mpi_iftype;
114 } static const mlx_pci_ident[] = {
115 	{
116 		PCI_VENDOR_MYLEX,
117 		PCI_PRODUCT_MYLEX_RAID_V2,
118 		0x0000,
119 		0x0000,
120 		2,
121 	},
122 	{
123 		PCI_VENDOR_MYLEX,
124 		PCI_PRODUCT_MYLEX_RAID_V3,
125 		0x0000,
126 		0x0000,
127 		3,
128 	},
129 	{
130 		PCI_VENDOR_MYLEX,
131 		PCI_PRODUCT_MYLEX_RAID_V4,
132 		0x0000,
133 		0x0000,
134 		4,
135 	},
136 	{
137 		PCI_VENDOR_DEC,
138 		PCI_PRODUCT_DEC_SWXCR,
139 		PCI_VENDOR_MYLEX,
140 		PCI_PRODUCT_MYLEX_RAID_V5,
141 		5,
142 	},
143 };
144 
145 struct cfattach mlx_pci_ca = {
146 	sizeof(struct mlx_softc), mlx_pci_match, mlx_pci_attach
147 };
148 
149 /*
150  * Try to find a `mlx_pci_ident' entry corresponding to this board.
151  */
152 static const struct mlx_pci_ident *
153 mlx_pci_findmpi(struct pci_attach_args *pa)
154 {
155 	const struct mlx_pci_ident *mpi, *maxmpi;
156 	pcireg_t reg;
157 
158 	mpi = mlx_pci_ident;
159 	maxmpi = mpi + sizeof(mlx_pci_ident) / sizeof(mlx_pci_ident[0]);
160 
161 	for (; mpi < maxmpi; mpi++) {
162 		if (PCI_VENDOR(pa->pa_id) != mpi->mpi_vendor ||
163 		    PCI_PRODUCT(pa->pa_id) != mpi->mpi_product)
164 			continue;
165 
166 		if (mpi->mpi_subvendor == 0x0000)
167 			return (mpi);
168 
169 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
170 
171 		if (PCI_VENDOR(reg) == mpi->mpi_subvendor &&
172 		    PCI_PRODUCT(reg) == mpi->mpi_subproduct)
173 			return (mpi);
174 	}
175 
176 	return (NULL);
177 }
178 
179 /*
180  * Match a supported board.
181  */
182 static int
183 mlx_pci_match(struct device *parent, struct cfdata *cfdata, void *aux)
184 {
185 
186 	return (mlx_pci_findmpi(aux) != NULL);
187 }
188 
189 /*
190  * Attach a supported board.
191  */
192 static void
193 mlx_pci_attach(struct device *parent, struct device *self, void *aux)
194 {
195 	struct pci_attach_args *pa;
196 	struct mlx_softc *mlx;
197 	pci_chipset_tag_t pc;
198 	pci_intr_handle_t ih;
199 	pcireg_t reg;
200 	const char *intrstr;
201 	int ior, memr, i;
202 	const struct mlx_pci_ident *mpi;
203 
204 	mlx = (struct mlx_softc *)self;
205 	pa = aux;
206 	pc = pa->pa_pc;
207 	mpi = mlx_pci_findmpi(aux);
208 
209 	mlx->mlx_dmat = pa->pa_dmat;
210 	mlx->mlx_iftype = mpi->mpi_iftype;
211 
212 	printf(": Mylex RAID (v%d interface)\n", mpi->mpi_iftype);
213 
214 	/*
215 	 * Map the PCI register window.
216 	 */
217 	memr = -1;
218 	ior = -1;
219 
220 	for (i = 0x10; i <= 0x14; i += 4) {
221 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, i);
222 
223 		if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) {
224 			if (ior == -1 && PCI_MAPREG_IO_SIZE(reg) != 0)
225 				ior = i;
226 		} else {
227 			if (memr == -1 && PCI_MAPREG_MEM_SIZE(reg) != 0)
228 				memr = i;
229 		}
230 	}
231 
232 	if (memr != -1)
233 		if (pci_mapreg_map(pa, memr, PCI_MAPREG_TYPE_MEM, 0,
234 		    &mlx->mlx_iot, &mlx->mlx_ioh, NULL, NULL))
235 			memr = -1;
236 	if (ior != -1)
237 		if (pci_mapreg_map(pa, ior, PCI_MAPREG_TYPE_IO, 0,
238 		    &mlx->mlx_iot, &mlx->mlx_ioh, NULL, NULL))
239 		    	ior = -1;
240 	if (memr == -1 && ior == -1) {
241 		printf("%s: can't map i/o or memory space\n", self->dv_xname);
242 		return;
243 	}
244 
245 	/* Enable the device. */
246 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
247 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
248 	    reg | PCI_COMMAND_MASTER_ENABLE);
249 
250 	/* Map and establish the interrupt. */
251 	if (pci_intr_map(pa, &ih)) {
252 		printf("%s: can't map interrupt\n", self->dv_xname);
253 		return;
254 	}
255 	intrstr = pci_intr_string(pc, ih);
256 	mlx->mlx_ih = pci_intr_establish(pc, ih, IPL_BIO, mlx_intr, mlx);
257 	if (mlx->mlx_ih == NULL) {
258 		printf("%s: can't establish interrupt", self->dv_xname);
259 		if (intrstr != NULL)
260 			printf(" at %s", intrstr);
261 		printf("\n");
262 		return;
263 	}
264 
265 	/* Select linkage based on controller interface type. */
266 	switch (mlx->mlx_iftype) {
267 	case 2:
268 	case 3:
269 		mlx->mlx_submit = mlx_v3_submit;
270 		mlx->mlx_findcomplete = mlx_v3_findcomplete;
271 		mlx->mlx_intaction = mlx_v3_intaction;
272 		mlx->mlx_fw_handshake = mlx_v3_fw_handshake;
273 		break;
274 
275 	case 4:
276 		mlx->mlx_submit = mlx_v4_submit;
277 		mlx->mlx_findcomplete = mlx_v4_findcomplete;
278 		mlx->mlx_intaction = mlx_v4_intaction;
279 		mlx->mlx_fw_handshake = mlx_v4_fw_handshake;
280 		break;
281 
282 	case 5:
283 		mlx->mlx_submit = mlx_v5_submit;
284 		mlx->mlx_findcomplete = mlx_v5_findcomplete;
285 		mlx->mlx_intaction = mlx_v5_intaction;
286 		mlx->mlx_fw_handshake = mlx_v5_fw_handshake;
287 		break;
288 	}
289 
290 	mlx_init(mlx, intrstr);
291 }
292 
293 /*
294  * ================= V3 interface linkage =================
295  */
296 
297 /*
298  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on
299  * failure (the controller is not ready to take a command).
300  *
301  * Must be called at splbio or in a fashion that prevents reentry.
302  */
303 static int
304 mlx_v3_submit(struct mlx_softc *mlx, struct mlx_ccb *mc)
305 {
306 
307 	/* Ready for our command? */
308 	if ((mlx_inb(mlx, MLX_V3REG_IDB) & MLX_V3_IDB_FULL) == 0) {
309 		/* Copy mailbox data to window. */
310 		bus_space_write_region_1(mlx->mlx_iot, mlx->mlx_ioh,
311 		    MLX_V3REG_MAILBOX, mc->mc_mbox, MLX_V3_MAILBOX_LEN);
312 		bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh,
313 		    MLX_V3REG_MAILBOX, MLX_V3_MAILBOX_LEN,
314 		    BUS_SPACE_BARRIER_WRITE);
315 
316 		/* Post command. */
317 		mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_FULL);
318 		return (1);
319 	}
320 
321 	return (0);
322 }
323 
324 /*
325  * See if a command has been completed, if so acknowledge its completion and
326  * recover the slot number and status code.
327  *
328  * Must be called at splbio or in a fashion that prevents reentry.
329  */
330 static int
331 mlx_v3_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status)
332 {
333 
334 	/* Status available? */
335 	if ((mlx_inb(mlx, MLX_V3REG_ODB) & MLX_V3_ODB_SAVAIL) != 0) {
336 		*slot = mlx_inb(mlx, MLX_V3REG_STATUS_IDENT);
337 		*status = mlx_inw(mlx, MLX_V3REG_STATUS);
338 
339 		/* Acknowledge completion. */
340 		mlx_outb(mlx, MLX_V3REG_ODB, MLX_V3_ODB_SAVAIL);
341 		mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_SACK);
342 		return (1);
343 	}
344 
345 	return (0);
346 }
347 
348 /*
349  * Enable/disable interrupts as requested. (No acknowledge required)
350  *
351  * Must be called at splbio or in a fashion that prevents reentry.
352  */
353 static void
354 mlx_v3_intaction(struct mlx_softc *mlx, int action)
355 {
356 
357 	mlx_outb(mlx, MLX_V3REG_IE, action != 0);
358 }
359 
360 /*
361  * Poll for firmware error codes during controller initialisation.
362  *
363  * Returns 0 if initialisation is complete, 1 if still in progress but no
364  * error has been fetched, 2 if an error has been retrieved.
365  */
366 static int
367 mlx_v3_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2)
368 {
369 	u_int8_t fwerror;
370 
371 	/* First time around, clear any hardware completion status. */
372 	if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) {
373 		mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_SACK);
374 		DELAY(1000);
375 		mlx->mlx_flags |= MLXF_FW_INITTED;
376 	}
377 
378 	/* Init in progress? */
379 	if ((mlx_inb(mlx, MLX_V3REG_IDB) & MLX_V3_IDB_INIT_BUSY) == 0)
380 		return (0);
381 
382 	/* Test error value. */
383 	fwerror = mlx_inb(mlx, MLX_V3REG_FWERROR);
384 
385 	if ((fwerror & MLX_V3_FWERROR_PEND) == 0)
386 		return (1);
387 
388 	/* Mask status pending bit, fetch status. */
389 	*error = fwerror & ~MLX_V3_FWERROR_PEND;
390 	*param1 = mlx_inb(mlx, MLX_V3REG_FWERROR_PARAM1);
391 	*param2 = mlx_inb(mlx, MLX_V3REG_FWERROR_PARAM2);
392 
393 	/* Acknowledge. */
394 	mlx_outb(mlx, MLX_V3REG_FWERROR, 0);
395 
396 	return (2);
397 }
398 
399 /*
400  * ================= V4 interface linkage =================
401  */
402 
403 /*
404  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on
405  * failure (the controller is not ready to take a command).
406  *
407  * Must be called at splbio or in a fashion that prevents reentry.
408  */
409 static int
410 mlx_v4_submit(struct mlx_softc *mlx, struct mlx_ccb *mc)
411 {
412 
413 	/* Ready for our command? */
414 	if ((mlx_inl(mlx, MLX_V4REG_IDB) & MLX_V4_IDB_FULL) == 0) {
415 		/* Copy mailbox data to window. */
416 		bus_space_write_region_4(mlx->mlx_iot, mlx->mlx_ioh,
417 		    MLX_V4REG_MAILBOX, (const u_int32_t *)mc->mc_mbox,
418 		    MLX_V4_MAILBOX_LEN >> 2);
419 		bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh,
420 		    MLX_V4REG_MAILBOX, MLX_V4_MAILBOX_LEN,
421 		    BUS_SPACE_BARRIER_WRITE);
422 
423 		/* Post command. */
424 		mlx_outl(mlx, MLX_V4REG_IDB, MLX_V4_IDB_HWMBOX_CMD);
425 		return (1);
426 	}
427 
428 	return (0);
429 }
430 
431 /*
432  * See if a command has been completed, if so acknowledge its completion and
433  * recover the slot number and status code.
434  *
435  * Must be called at splbio or in a fashion that prevents reentry.
436  */
437 static int
438 mlx_v4_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status)
439 {
440 
441 	/* Status available? */
442 	if ((mlx_inl(mlx, MLX_V4REG_ODB) & MLX_V4_ODB_HWSAVAIL) != 0) {
443 		*slot = mlx_inb(mlx, MLX_V4REG_STATUS_IDENT);
444 		*status = mlx_inw(mlx, MLX_V4REG_STATUS);
445 
446 		/* Acknowledge completion. */
447 		mlx_outl(mlx, MLX_V4REG_ODB, MLX_V4_ODB_HWMBOX_ACK);
448 		mlx_outl(mlx, MLX_V4REG_IDB, MLX_V4_IDB_SACK);
449 		return (1);
450 	}
451 
452 	return (0);
453 }
454 
455 /*
456  * Enable/disable interrupts as requested.
457  *
458  * Must be called at splbio or in a fashion that prevents reentry.
459  */
460 static void
461 mlx_v4_intaction(struct mlx_softc *mlx, int action)
462 {
463 	u_int32_t ier;
464 
465 	if (!action)
466 		ier = MLX_V4_IE_MASK | MLX_V4_IE_DISINT;
467 	else
468 		ier = MLX_V4_IE_MASK & ~MLX_V4_IE_DISINT;
469 
470 	mlx_outl(mlx, MLX_V4REG_IE, ier);
471 }
472 
473 /*
474  * Poll for firmware error codes during controller initialisation.
475  *
476  * Returns 0 if initialisation is complete, 1 if still in progress but no
477  * error has been fetched, 2 if an error has been retrieved.
478  */
479 static int
480 mlx_v4_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2)
481 {
482 	u_int8_t fwerror;
483 
484 	/* First time around, clear any hardware completion status. */
485 	if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) {
486 		mlx_outl(mlx, MLX_V4REG_IDB, MLX_V4_IDB_SACK);
487 		DELAY(1000);
488 		mlx->mlx_flags |= MLXF_FW_INITTED;
489 	}
490 
491 	/* Init in progress? */
492 	if ((mlx_inl(mlx, MLX_V4REG_IDB) & MLX_V4_IDB_INIT_BUSY) == 0)
493 		return (0);
494 
495 	/* Test error value */
496 	fwerror = mlx_inb(mlx, MLX_V4REG_FWERROR);
497 	if ((fwerror & MLX_V4_FWERROR_PEND) == 0)
498 		return (1);
499 
500 	/* Mask status pending bit, fetch status. */
501 	*error = fwerror & ~MLX_V4_FWERROR_PEND;
502 	*param1 = mlx_inb(mlx, MLX_V4REG_FWERROR_PARAM1);
503 	*param2 = mlx_inb(mlx, MLX_V4REG_FWERROR_PARAM2);
504 
505 	/* Acknowledge. */
506 	mlx_outb(mlx, MLX_V4REG_FWERROR, 0);
507 
508 	return (2);
509 }
510 
511 /*
512  * ================= V5 interface linkage =================
513  */
514 
515 /*
516  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
517  * (the controller is not ready to take a command).
518  *
519  * Must be called at splbio or in a fashion that prevents reentry.
520  */
521 static int
522 mlx_v5_submit(struct mlx_softc *mlx, struct mlx_ccb *mc)
523 {
524 
525 	/* Ready for our command? */
526 	if ((mlx_inb(mlx, MLX_V5REG_IDB) & MLX_V5_IDB_EMPTY) != 0) {
527 		/* Copy mailbox data to window. */
528 		bus_space_write_region_4(mlx->mlx_iot, mlx->mlx_ioh,
529 		    MLX_V5REG_MAILBOX, (const u_int32_t *)mc->mc_mbox,
530 		    MLX_V5_MAILBOX_LEN >> 2);
531 		bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh,
532 		    MLX_V5REG_MAILBOX, MLX_V5_MAILBOX_LEN,
533 		    BUS_SPACE_BARRIER_WRITE);
534 
535 		/* Post command */
536 		mlx_outb(mlx, MLX_V5REG_IDB, MLX_V5_IDB_HWMBOX_CMD);
537 		return (1);
538 	}
539 
540 	return (0);
541 }
542 
543 /*
544  * See if a command has been completed, if so acknowledge its completion and
545  * recover the slot number and status code.
546  *
547  * Must be called at splbio or in a fashion that prevents reentry.
548  */
549 static int
550 mlx_v5_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status)
551 {
552 
553 	/* Status available? */
554 	if ((mlx_inb(mlx, MLX_V5REG_ODB) & MLX_V5_ODB_HWSAVAIL) != 0) {
555 		*slot = mlx_inb(mlx, MLX_V5REG_STATUS_IDENT);
556 		*status = mlx_inw(mlx, MLX_V5REG_STATUS);
557 
558 		/* Acknowledge completion. */
559 		mlx_outb(mlx, MLX_V5REG_ODB, MLX_V5_ODB_HWMBOX_ACK);
560 		mlx_outb(mlx, MLX_V5REG_IDB, MLX_V5_IDB_SACK);
561 		return (1);
562 	}
563 
564 	return (0);
565 }
566 
567 /*
568  * Enable/disable interrupts as requested.
569  *
570  * Must be called at splbio or in a fashion that prevents reentry.
571  */
572 static void
573 mlx_v5_intaction(struct mlx_softc *mlx, int action)
574 {
575 	u_int8_t ier;
576 
577 	if (!action)
578 		ier = 0xff & MLX_V5_IE_DISINT;
579 	else
580 		ier = 0xff & ~MLX_V5_IE_DISINT;
581 
582 	mlx_outb(mlx, MLX_V5REG_IE, ier);
583 }
584 
585 /*
586  * Poll for firmware error codes during controller initialisation.
587  *
588  * Returns 0 if initialisation is complete, 1 if still in progress but no
589  * error has been fetched, 2 if an error has been retrieved.
590  */
591 static int
592 mlx_v5_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2)
593 {
594 	u_int8_t fwerror;
595 
596 	/* First time around, clear any hardware completion status. */
597 	if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) {
598 		mlx_outb(mlx, MLX_V5REG_IDB, MLX_V5_IDB_SACK);
599 		DELAY(1000);
600 		mlx->mlx_flags |= MLXF_FW_INITTED;
601 	}
602 
603 	/* Init in progress? */
604 	if ((mlx_inb(mlx, MLX_V5REG_IDB) & MLX_V5_IDB_INIT_DONE) != 0)
605 		return (0);
606 
607 	/* Test for error value. */
608 	fwerror = mlx_inb(mlx, MLX_V5REG_FWERROR);
609 	if ((fwerror & MLX_V5_FWERROR_PEND) == 0)
610 		return (1);
611 
612 	/* Mask status pending bit, fetch status. */
613 	*error = fwerror & ~MLX_V5_FWERROR_PEND;
614 	*param1 = mlx_inb(mlx, MLX_V5REG_FWERROR_PARAM1);
615 	*param2 = mlx_inb(mlx, MLX_V5REG_FWERROR_PARAM2);
616 
617 	/* Acknowledge. */
618 	mlx_outb(mlx, MLX_V5REG_FWERROR, 0xff);
619 
620 	return (2);
621 }
622