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