xref: /netbsd-src/sys/dev/pci/mlx_pci.c (revision 17306b8fd0952c7489f93f0230818481e5a1e2c9)
1 /*	$NetBSD: mlx_pci.c,v 1.3 2001/05/15 12:49:37 ad 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 #ifdef	MLX_RESET
98 static int	mlx_v3_reset(struct mlx_softc *);
99 #endif
100 
101 static int	mlx_v4_submit(struct mlx_softc *, struct mlx_ccb *);
102 static int	mlx_v4_findcomplete(struct mlx_softc *, u_int *, u_int *);
103 static void	mlx_v4_intaction(struct mlx_softc *, int);
104 static int	mlx_v4_fw_handshake(struct mlx_softc *, int *, int *, int *);
105 
106 static int	mlx_v5_submit(struct mlx_softc *, struct mlx_ccb *);
107 static int	mlx_v5_findcomplete(struct mlx_softc *, u_int *, u_int *);
108 static void	mlx_v5_intaction(struct mlx_softc *, int);
109 static int	mlx_v5_fw_handshake(struct mlx_softc *, int *, int *, int *);
110 
111 struct mlx_pci_ident {
112 	u_short	mpi_vendor;
113 	u_short	mpi_product;
114 	u_short	mpi_subvendor;
115 	u_short	mpi_subproduct;
116 	int	mpi_iftype;
117 } static const mlx_pci_ident[] = {
118 	{
119 		PCI_VENDOR_MYLEX,
120 		PCI_PRODUCT_MYLEX_RAID_V2,
121 		0x0000,
122 		0x0000,
123 		2,
124 	},
125 	{
126 		PCI_VENDOR_MYLEX,
127 		PCI_PRODUCT_MYLEX_RAID_V3,
128 		0x0000,
129 		0x0000,
130 		3,
131 	},
132 	{
133 		PCI_VENDOR_MYLEX,
134 		PCI_PRODUCT_MYLEX_RAID_V4,
135 		0x0000,
136 		0x0000,
137 		4,
138 	},
139 	{
140 		PCI_VENDOR_DEC,
141 		PCI_PRODUCT_DEC_SWXCR,
142 		PCI_VENDOR_MYLEX,
143 		PCI_PRODUCT_MYLEX_RAID_V5,
144 		5,
145 	},
146 };
147 
148 struct cfattach mlx_pci_ca = {
149 	sizeof(struct mlx_softc), mlx_pci_match, mlx_pci_attach
150 };
151 
152 /*
153  * Try to find a `mlx_pci_ident' entry corresponding to this board.
154  */
155 static const struct mlx_pci_ident *
156 mlx_pci_findmpi(struct pci_attach_args *pa)
157 {
158 	const struct mlx_pci_ident *mpi, *maxmpi;
159 	pcireg_t reg;
160 
161 	mpi = mlx_pci_ident;
162 	maxmpi = mpi + sizeof(mlx_pci_ident) / sizeof(mlx_pci_ident[0]);
163 
164 	for (; mpi < maxmpi; mpi++) {
165 		if (PCI_VENDOR(pa->pa_id) != mpi->mpi_vendor ||
166 		    PCI_PRODUCT(pa->pa_id) != mpi->mpi_product)
167 			continue;
168 
169 		if (mpi->mpi_subvendor == 0x0000)
170 			return (mpi);
171 
172 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
173 
174 		if (PCI_VENDOR(reg) == mpi->mpi_subvendor &&
175 		    PCI_PRODUCT(reg) == mpi->mpi_subproduct)
176 			return (mpi);
177 	}
178 
179 	return (NULL);
180 }
181 
182 /*
183  * Match a supported board.
184  */
185 static int
186 mlx_pci_match(struct device *parent, struct cfdata *cfdata, void *aux)
187 {
188 
189 	return (mlx_pci_findmpi(aux) != NULL);
190 }
191 
192 /*
193  * Attach a supported board.
194  */
195 static void
196 mlx_pci_attach(struct device *parent, struct device *self, void *aux)
197 {
198 	struct pci_attach_args *pa;
199 	struct mlx_softc *mlx;
200 	pci_chipset_tag_t pc;
201 	pci_intr_handle_t ih;
202 	pcireg_t reg;
203 	const char *intrstr;
204 	int ior, memr, i;
205 	const struct mlx_pci_ident *mpi;
206 
207 	mlx = (struct mlx_softc *)self;
208 	pa = aux;
209 	pc = pa->pa_pc;
210 	mpi = mlx_pci_findmpi(aux);
211 
212 	mlx->mlx_dmat = pa->pa_dmat;
213 	mlx->mlx_iftype = mpi->mpi_iftype;
214 
215 	printf(": Mylex RAID (v%d interface)\n", mpi->mpi_iftype);
216 
217 	/*
218 	 * Map the PCI register window.
219 	 */
220 	memr = -1;
221 	ior = -1;
222 
223 	for (i = 0x10; i <= 0x14; i += 4) {
224 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, i);
225 
226 		if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) {
227 			if (ior == -1 && PCI_MAPREG_IO_SIZE(reg) != 0)
228 				ior = i;
229 		} else {
230 			if (memr == -1 && PCI_MAPREG_MEM_SIZE(reg) != 0)
231 				memr = i;
232 		}
233 	}
234 
235 	if (memr != -1)
236 		if (pci_mapreg_map(pa, memr, PCI_MAPREG_TYPE_MEM, 0,
237 		    &mlx->mlx_iot, &mlx->mlx_ioh, NULL, NULL))
238 			memr = -1;
239 	if (ior != -1)
240 		if (pci_mapreg_map(pa, ior, PCI_MAPREG_TYPE_IO, 0,
241 		    &mlx->mlx_iot, &mlx->mlx_ioh, NULL, NULL))
242 		    	ior = -1;
243 	if (memr == -1 && ior == -1) {
244 		printf("%s: can't map i/o or memory space\n", self->dv_xname);
245 		return;
246 	}
247 
248 	/* Enable the device. */
249 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
250 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
251 	    reg | PCI_COMMAND_MASTER_ENABLE);
252 
253 	/* Map and establish the interrupt. */
254 	if (pci_intr_map(pa, &ih)) {
255 		printf("%s: can't map interrupt\n", self->dv_xname);
256 		return;
257 	}
258 	intrstr = pci_intr_string(pc, ih);
259 	mlx->mlx_ih = pci_intr_establish(pc, ih, IPL_BIO, mlx_intr, mlx);
260 	if (mlx->mlx_ih == NULL) {
261 		printf("%s: can't establish interrupt", self->dv_xname);
262 		if (intrstr != NULL)
263 			printf(" at %s", intrstr);
264 		printf("\n");
265 		return;
266 	}
267 
268 	/* Select linkage based on controller interface type. */
269 	switch (mlx->mlx_iftype) {
270 	case 2:
271 	case 3:
272 		mlx->mlx_submit = mlx_v3_submit;
273 		mlx->mlx_findcomplete = mlx_v3_findcomplete;
274 		mlx->mlx_intaction = mlx_v3_intaction;
275 		mlx->mlx_fw_handshake = mlx_v3_fw_handshake;
276 #ifdef MLX_RESET
277 		mlx->mlx_reset = mlx_v3_reset;
278 #endif
279 		break;
280 
281 	case 4:
282 		mlx->mlx_submit = mlx_v4_submit;
283 		mlx->mlx_findcomplete = mlx_v4_findcomplete;
284 		mlx->mlx_intaction = mlx_v4_intaction;
285 		mlx->mlx_fw_handshake = mlx_v4_fw_handshake;
286 		break;
287 
288 	case 5:
289 		mlx->mlx_submit = mlx_v5_submit;
290 		mlx->mlx_findcomplete = mlx_v5_findcomplete;
291 		mlx->mlx_intaction = mlx_v5_intaction;
292 		mlx->mlx_fw_handshake = mlx_v5_fw_handshake;
293 		break;
294 	}
295 
296 	mlx_init(mlx, intrstr);
297 }
298 
299 /*
300  * ================= V3 interface linkage =================
301  */
302 
303 /*
304  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on
305  * failure (the controller is not ready to take a command).
306  *
307  * Must be called at splbio or in a fashion that prevents reentry.
308  */
309 static int
310 mlx_v3_submit(struct mlx_softc *mlx, struct mlx_ccb *mc)
311 {
312 
313 	/* Ready for our command? */
314 	if ((mlx_inb(mlx, MLX_V3REG_IDB) & MLX_V3_IDB_FULL) == 0) {
315 		/* Copy mailbox data to window. */
316 		bus_space_write_region_1(mlx->mlx_iot, mlx->mlx_ioh,
317 		    MLX_V3REG_MAILBOX, mc->mc_mbox, MLX_V3_MAILBOX_LEN);
318 		bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh,
319 		    MLX_V3REG_MAILBOX, MLX_V3_MAILBOX_LEN,
320 		    BUS_SPACE_BARRIER_WRITE);
321 
322 		/* Post command. */
323 		mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_FULL);
324 		return (1);
325 	}
326 
327 	return (0);
328 }
329 
330 /*
331  * See if a command has been completed, if so acknowledge its completion and
332  * recover the slot number and status code.
333  *
334  * Must be called at splbio or in a fashion that prevents reentry.
335  */
336 static int
337 mlx_v3_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status)
338 {
339 
340 	/* Status available? */
341 	if ((mlx_inb(mlx, MLX_V3REG_ODB) & MLX_V3_ODB_SAVAIL) != 0) {
342 		*slot = mlx_inb(mlx, MLX_V3REG_STATUS_IDENT);
343 		*status = mlx_inw(mlx, MLX_V3REG_STATUS);
344 
345 		/* Acknowledge completion. */
346 		mlx_outb(mlx, MLX_V3REG_ODB, MLX_V3_ODB_SAVAIL);
347 		mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_SACK);
348 		return (1);
349 	}
350 
351 	return (0);
352 }
353 
354 /*
355  * Enable/disable interrupts as requested. (No acknowledge required)
356  *
357  * Must be called at splbio or in a fashion that prevents reentry.
358  */
359 static void
360 mlx_v3_intaction(struct mlx_softc *mlx, int action)
361 {
362 
363 	mlx_outb(mlx, MLX_V3REG_IE, action != 0);
364 }
365 
366 /*
367  * Poll for firmware error codes during controller initialisation.
368  *
369  * Returns 0 if initialisation is complete, 1 if still in progress but no
370  * error has been fetched, 2 if an error has been retrieved.
371  */
372 static int
373 mlx_v3_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2)
374 {
375 	u_int8_t fwerror;
376 
377 	/* First time around, clear any hardware completion status. */
378 	if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) {
379 		mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_SACK);
380 		DELAY(1000);
381 		mlx->mlx_flags |= MLXF_FW_INITTED;
382 	}
383 
384 	/* Init in progress? */
385 	if ((mlx_inb(mlx, MLX_V3REG_IDB) & MLX_V3_IDB_INIT_BUSY) == 0)
386 		return (0);
387 
388 	/* Test error value. */
389 	fwerror = mlx_inb(mlx, MLX_V3REG_FWERROR);
390 
391 	if ((fwerror & MLX_V3_FWERROR_PEND) == 0)
392 		return (1);
393 
394 	/* Mask status pending bit, fetch status. */
395 	*error = fwerror & ~MLX_V3_FWERROR_PEND;
396 	*param1 = mlx_inb(mlx, MLX_V3REG_FWERROR_PARAM1);
397 	*param2 = mlx_inb(mlx, MLX_V3REG_FWERROR_PARAM2);
398 
399 	/* Acknowledge. */
400 	mlx_outb(mlx, MLX_V3REG_FWERROR, 0);
401 
402 	return (2);
403 }
404 
405 #ifdef MLX_RESET
406 /*
407  * Reset the controller.  Return non-zero on failure.
408  */
409 static int
410 mlx_v3_reset(struct mlx_softc *mlx)
411 {
412 	int i;
413 
414 	mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_SACK);
415 	delay(1000000);
416 
417 	/* Wait up to 2 minutes for the bit to clear. */
418 	for (i = 120; i != 0; i--) {
419 		delay(1000000);
420 		if ((mlx_inb(mlx, MLX_V3REG_IDB) & MLX_V3_IDB_SACK) == 0)
421 			break;
422 	}
423 	if (i == 0) {
424 		/* ZZZ */
425 		printf("mlx0: SACK didn't clear\n");
426 		return (-1);
427 	}
428 
429 	mlx_outb(mlx, MLX_V3REG_IDB, MLX_V3_IDB_RESET);
430 
431 	/* Wait up to 5 seconds for the bit to clear. */
432 	for (i = 5; i != 0; i--) {
433 		delay(1000000);
434 		if ((mlx_inb(mlx, MLX_V3REG_IDB) & MLX_V3_IDB_RESET) == 0)
435 			break;
436 	}
437 	if (i == 0) {
438 		/* ZZZ */
439 		printf("mlx0: RESET didn't clear\n");
440 		return (-1);
441 	}
442 
443 	return (0);
444 }
445 #endif	/* MLX_RESET */
446 
447 /*
448  * ================= V4 interface linkage =================
449  */
450 
451 /*
452  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on
453  * failure (the controller is not ready to take a command).
454  *
455  * Must be called at splbio or in a fashion that prevents reentry.
456  */
457 static int
458 mlx_v4_submit(struct mlx_softc *mlx, struct mlx_ccb *mc)
459 {
460 
461 	/* Ready for our command? */
462 	if ((mlx_inl(mlx, MLX_V4REG_IDB) & MLX_V4_IDB_FULL) == 0) {
463 		/* Copy mailbox data to window. */
464 		bus_space_write_region_1(mlx->mlx_iot, mlx->mlx_ioh,
465 		    MLX_V4REG_MAILBOX, mc->mc_mbox, MLX_V4_MAILBOX_LEN);
466 		bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh,
467 		    MLX_V4REG_MAILBOX, MLX_V4_MAILBOX_LEN,
468 		    BUS_SPACE_BARRIER_WRITE);
469 
470 		/* Post command. */
471 		mlx_outl(mlx, MLX_V4REG_IDB, MLX_V4_IDB_HWMBOX_CMD);
472 		return (1);
473 	}
474 
475 	return (0);
476 }
477 
478 /*
479  * See if a command has been completed, if so acknowledge its completion and
480  * recover the slot number and status code.
481  *
482  * Must be called at splbio or in a fashion that prevents reentry.
483  */
484 static int
485 mlx_v4_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status)
486 {
487 
488 	/* Status available? */
489 	if ((mlx_inl(mlx, MLX_V4REG_ODB) & MLX_V4_ODB_HWSAVAIL) != 0) {
490 		*slot = mlx_inb(mlx, MLX_V4REG_STATUS_IDENT);
491 		*status = mlx_inw(mlx, MLX_V4REG_STATUS);
492 
493 		/* Acknowledge completion. */
494 		mlx_outl(mlx, MLX_V4REG_ODB, MLX_V4_ODB_HWMBOX_ACK);
495 		mlx_outl(mlx, MLX_V4REG_IDB, MLX_V4_IDB_SACK);
496 		return (1);
497 	}
498 
499 	return (0);
500 }
501 
502 /*
503  * Enable/disable interrupts as requested.
504  *
505  * Must be called at splbio or in a fashion that prevents reentry.
506  */
507 static void
508 mlx_v4_intaction(struct mlx_softc *mlx, int action)
509 {
510 	u_int32_t ier;
511 
512 	if (!action)
513 		ier = MLX_V4_IE_MASK | MLX_V4_IE_DISINT;
514 	else
515 		ier = MLX_V4_IE_MASK & ~MLX_V4_IE_DISINT;
516 
517 	mlx_outl(mlx, MLX_V4REG_IE, ier);
518 }
519 
520 /*
521  * Poll for firmware error codes during controller initialisation.
522  *
523  * Returns 0 if initialisation is complete, 1 if still in progress but no
524  * error has been fetched, 2 if an error has been retrieved.
525  */
526 static int
527 mlx_v4_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2)
528 {
529 	u_int8_t fwerror;
530 
531 	/* First time around, clear any hardware completion status. */
532 	if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) {
533 		mlx_outl(mlx, MLX_V4REG_IDB, MLX_V4_IDB_SACK);
534 		DELAY(1000);
535 		mlx->mlx_flags |= MLXF_FW_INITTED;
536 	}
537 
538 	/* Init in progress? */
539 	if ((mlx_inl(mlx, MLX_V4REG_IDB) & MLX_V4_IDB_INIT_BUSY) == 0)
540 		return (0);
541 
542 	/* Test error value */
543 	fwerror = mlx_inb(mlx, MLX_V4REG_FWERROR);
544 	if ((fwerror & MLX_V4_FWERROR_PEND) == 0)
545 		return (1);
546 
547 	/* Mask status pending bit, fetch status. */
548 	*error = fwerror & ~MLX_V4_FWERROR_PEND;
549 	*param1 = mlx_inb(mlx, MLX_V4REG_FWERROR_PARAM1);
550 	*param2 = mlx_inb(mlx, MLX_V4REG_FWERROR_PARAM2);
551 
552 	/* Acknowledge. */
553 	mlx_outb(mlx, MLX_V4REG_FWERROR, 0);
554 
555 	return (2);
556 }
557 
558 /*
559  * ================= V5 interface linkage =================
560  */
561 
562 /*
563  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
564  * (the controller is not ready to take a command).
565  *
566  * Must be called at splbio or in a fashion that prevents reentry.
567  */
568 static int
569 mlx_v5_submit(struct mlx_softc *mlx, struct mlx_ccb *mc)
570 {
571 
572 	/* Ready for our command? */
573 	if ((mlx_inb(mlx, MLX_V5REG_IDB) & MLX_V5_IDB_EMPTY) != 0) {
574 		/* Copy mailbox data to window. */
575 		bus_space_write_region_1(mlx->mlx_iot, mlx->mlx_ioh,
576 		    MLX_V5REG_MAILBOX, mc->mc_mbox, MLX_V5_MAILBOX_LEN);
577 		bus_space_barrier(mlx->mlx_iot, mlx->mlx_ioh,
578 		    MLX_V5REG_MAILBOX, MLX_V5_MAILBOX_LEN,
579 		    BUS_SPACE_BARRIER_WRITE);
580 
581 		/* Post command */
582 		mlx_outb(mlx, MLX_V5REG_IDB, MLX_V5_IDB_HWMBOX_CMD);
583 		return (1);
584 	}
585 
586 	return (0);
587 }
588 
589 /*
590  * See if a command has been completed, if so acknowledge its completion and
591  * recover the slot number and status code.
592  *
593  * Must be called at splbio or in a fashion that prevents reentry.
594  */
595 static int
596 mlx_v5_findcomplete(struct mlx_softc *mlx, u_int *slot, u_int *status)
597 {
598 
599 	/* Status available? */
600 	if ((mlx_inb(mlx, MLX_V5REG_ODB) & MLX_V5_ODB_HWSAVAIL) != 0) {
601 		*slot = mlx_inb(mlx, MLX_V5REG_STATUS_IDENT);
602 		*status = mlx_inw(mlx, MLX_V5REG_STATUS);
603 
604 		/* Acknowledge completion. */
605 		mlx_outb(mlx, MLX_V5REG_ODB, MLX_V5_ODB_HWMBOX_ACK);
606 		mlx_outb(mlx, MLX_V5REG_IDB, MLX_V5_IDB_SACK);
607 		return (1);
608 	}
609 
610 	return (0);
611 }
612 
613 /*
614  * Enable/disable interrupts as requested.
615  *
616  * Must be called at splbio or in a fashion that prevents reentry.
617  */
618 static void
619 mlx_v5_intaction(struct mlx_softc *mlx, int action)
620 {
621 	u_int8_t ier;
622 
623 	if (!action)
624 		ier = 0xff & MLX_V5_IE_DISINT;
625 	else
626 		ier = 0xff & ~MLX_V5_IE_DISINT;
627 
628 	mlx_outb(mlx, MLX_V5REG_IE, ier);
629 }
630 
631 /*
632  * Poll for firmware error codes during controller initialisation.
633  *
634  * Returns 0 if initialisation is complete, 1 if still in progress but no
635  * error has been fetched, 2 if an error has been retrieved.
636  */
637 static int
638 mlx_v5_fw_handshake(struct mlx_softc *mlx, int *error, int *param1, int *param2)
639 {
640 	u_int8_t fwerror;
641 
642 	/* First time around, clear any hardware completion status. */
643 	if ((mlx->mlx_flags & MLXF_FW_INITTED) == 0) {
644 		mlx_outb(mlx, MLX_V5REG_IDB, MLX_V5_IDB_SACK);
645 		DELAY(1000);
646 		mlx->mlx_flags |= MLXF_FW_INITTED;
647 	}
648 
649 	/* Init in progress? */
650 	if ((mlx_inb(mlx, MLX_V5REG_IDB) & MLX_V5_IDB_INIT_DONE) != 0)
651 		return (0);
652 
653 	/* Test for error value. */
654 	fwerror = mlx_inb(mlx, MLX_V5REG_FWERROR);
655 	if ((fwerror & MLX_V5_FWERROR_PEND) == 0)
656 		return (1);
657 
658 	/* Mask status pending bit, fetch status. */
659 	*error = fwerror & ~MLX_V5_FWERROR_PEND;
660 	*param1 = mlx_inb(mlx, MLX_V5REG_FWERROR_PARAM1);
661 	*param2 = mlx_inb(mlx, MLX_V5REG_FWERROR_PARAM2);
662 
663 	/* Acknowledge. */
664 	mlx_outb(mlx, MLX_V5REG_FWERROR, 0xff);
665 
666 	return (2);
667 }
668