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