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