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