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