11ac4b82bSMike Smith /*- 21ac4b82bSMike Smith * Copyright (c) 1999 Michael Smith 31ac4b82bSMike Smith * All rights reserved. 41ac4b82bSMike Smith * 51ac4b82bSMike Smith * Redistribution and use in source and binary forms, with or without 61ac4b82bSMike Smith * modification, are permitted provided that the following conditions 71ac4b82bSMike Smith * are met: 81ac4b82bSMike Smith * 1. Redistributions of source code must retain the above copyright 91ac4b82bSMike Smith * notice, this list of conditions and the following disclaimer. 101ac4b82bSMike Smith * 2. Redistributions in binary form must reproduce the above copyright 111ac4b82bSMike Smith * notice, this list of conditions and the following disclaimer in the 121ac4b82bSMike Smith * documentation and/or other materials provided with the distribution. 131ac4b82bSMike Smith * 141ac4b82bSMike Smith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151ac4b82bSMike Smith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161ac4b82bSMike Smith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171ac4b82bSMike Smith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181ac4b82bSMike Smith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191ac4b82bSMike Smith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201ac4b82bSMike Smith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211ac4b82bSMike Smith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221ac4b82bSMike Smith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231ac4b82bSMike Smith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241ac4b82bSMike Smith * SUCH DAMAGE. 251ac4b82bSMike Smith * 261ac4b82bSMike Smith * $FreeBSD$ 271ac4b82bSMike Smith */ 281ac4b82bSMike Smith 291ac4b82bSMike Smith /* 301ac4b82bSMike Smith * Driver for the Mylex DAC960 family of RAID controllers. 311ac4b82bSMike Smith */ 321ac4b82bSMike Smith 331ac4b82bSMike Smith #include <sys/param.h> 341ac4b82bSMike Smith #include <sys/systm.h> 351ac4b82bSMike Smith #include <sys/malloc.h> 361ac4b82bSMike Smith #include <sys/kernel.h> 371ac4b82bSMike Smith 381ac4b82bSMike Smith #include <sys/bus.h> 391ac4b82bSMike Smith #include <sys/conf.h> 40da8bb3a3SMike Smith #include <sys/stat.h> 411ac4b82bSMike Smith 421ac4b82bSMike Smith #include <machine/resource.h> 43786cd128SMike Smith #include <machine/bus_memio.h> 44786cd128SMike Smith #include <machine/bus_pio.h> 451ac4b82bSMike Smith #include <machine/bus.h> 461ac4b82bSMike Smith #include <machine/clock.h> 471ac4b82bSMike Smith #include <sys/rman.h> 481ac4b82bSMike Smith 49891619a6SPoul-Henning Kamp #include <geom/geom_disk.h> 50891619a6SPoul-Henning Kamp 5115fd5d22SMike Smith #include <dev/mlx/mlx_compat.h> 521ac4b82bSMike Smith #include <dev/mlx/mlxio.h> 531ac4b82bSMike Smith #include <dev/mlx/mlxvar.h> 541ac4b82bSMike Smith #include <dev/mlx/mlxreg.h> 551ac4b82bSMike Smith 561ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = { 57dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 58dc08ffecSPoul-Henning Kamp .d_flags = D_NEEDGIANT, 597ac40f5fSPoul-Henning Kamp .d_open = mlx_open, 607ac40f5fSPoul-Henning Kamp .d_close = mlx_close, 617ac40f5fSPoul-Henning Kamp .d_ioctl = mlx_ioctl, 627ac40f5fSPoul-Henning Kamp .d_name = "mlx", 631ac4b82bSMike Smith }; 641ac4b82bSMike Smith 651ac4b82bSMike Smith devclass_t mlx_devclass; 661ac4b82bSMike Smith 671ac4b82bSMike Smith /* 681ac4b82bSMike Smith * Per-interface accessor methods 691ac4b82bSMike Smith */ 701ac4b82bSMike Smith static int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 711ac4b82bSMike Smith static int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 721ac4b82bSMike Smith static void mlx_v3_intaction(struct mlx_softc *sc, int action); 73da8bb3a3SMike Smith static int mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 741ac4b82bSMike Smith 75f6b84b08SMike Smith static int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 76f6b84b08SMike Smith static int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 77f6b84b08SMike Smith static void mlx_v4_intaction(struct mlx_softc *sc, int action); 78da8bb3a3SMike Smith static int mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 79f6b84b08SMike Smith 805792b7feSMike Smith static int mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 815792b7feSMike Smith static int mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 825792b7feSMike Smith static void mlx_v5_intaction(struct mlx_softc *sc, int action); 83da8bb3a3SMike Smith static int mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 845792b7feSMike Smith 851ac4b82bSMike Smith /* 861ac4b82bSMike Smith * Status monitoring 871ac4b82bSMike Smith */ 881ac4b82bSMike Smith static void mlx_periodic(void *data); 891ac4b82bSMike Smith static void mlx_periodic_enquiry(struct mlx_command *mc); 901ac4b82bSMike Smith static void mlx_periodic_eventlog_poll(struct mlx_softc *sc); 911ac4b82bSMike Smith static void mlx_periodic_eventlog_respond(struct mlx_command *mc); 921ac4b82bSMike Smith static void mlx_periodic_rebuild(struct mlx_command *mc); 931ac4b82bSMike Smith 941ac4b82bSMike Smith /* 951ac4b82bSMike Smith * Channel Pause 961ac4b82bSMike Smith */ 971ac4b82bSMike Smith static void mlx_pause_action(struct mlx_softc *sc); 981ac4b82bSMike Smith static void mlx_pause_done(struct mlx_command *mc); 991ac4b82bSMike Smith 1001ac4b82bSMike Smith /* 1011ac4b82bSMike Smith * Command submission. 1021ac4b82bSMike Smith */ 1031ac4b82bSMike Smith static void *mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, 1041ac4b82bSMike Smith void (*complete)(struct mlx_command *mc)); 1051ac4b82bSMike Smith static int mlx_flush(struct mlx_softc *sc); 106421f2f7dSMike Smith static int mlx_check(struct mlx_softc *sc, int drive); 1071ac4b82bSMike Smith static int mlx_rebuild(struct mlx_softc *sc, int channel, int target); 1081ac4b82bSMike Smith static int mlx_wait_command(struct mlx_command *mc); 1091ac4b82bSMike Smith static int mlx_poll_command(struct mlx_command *mc); 1101b4404f9SScott Long void mlx_startio_cb(void *arg, 1111b4404f9SScott Long bus_dma_segment_t *segs, 1121b4404f9SScott Long int nsegments, int error); 1131ac4b82bSMike Smith static void mlx_startio(struct mlx_softc *sc); 1141ac4b82bSMike Smith static void mlx_completeio(struct mlx_command *mc); 1151b4404f9SScott Long static int mlx_user_command(struct mlx_softc *sc, 1161b4404f9SScott Long struct mlx_usercommand *mu); 1171b4404f9SScott Long void mlx_user_cb(void *arg, bus_dma_segment_t *segs, 1181b4404f9SScott Long int nsegments, int error); 1191ac4b82bSMike Smith 1201ac4b82bSMike Smith /* 1211ac4b82bSMike Smith * Command buffer allocation. 1221ac4b82bSMike Smith */ 1231ac4b82bSMike Smith static struct mlx_command *mlx_alloccmd(struct mlx_softc *sc); 1241ac4b82bSMike Smith static void mlx_releasecmd(struct mlx_command *mc); 1251ac4b82bSMike Smith static void mlx_freecmd(struct mlx_command *mc); 1261ac4b82bSMike Smith 1271ac4b82bSMike Smith /* 1281ac4b82bSMike Smith * Command management. 1291ac4b82bSMike Smith */ 1301ac4b82bSMike Smith static int mlx_getslot(struct mlx_command *mc); 1311b4404f9SScott Long static void mlx_setup_dmamap(struct mlx_command *mc, 1321b4404f9SScott Long bus_dma_segment_t *segs, 1331b4404f9SScott Long int nsegments, int error); 1341ac4b82bSMike Smith static void mlx_unmapcmd(struct mlx_command *mc); 1351ac4b82bSMike Smith static int mlx_start(struct mlx_command *mc); 1361ac4b82bSMike Smith static int mlx_done(struct mlx_softc *sc); 1371ac4b82bSMike Smith static void mlx_complete(struct mlx_softc *sc); 1381ac4b82bSMike Smith 1391ac4b82bSMike Smith /* 1401ac4b82bSMike Smith * Debugging. 1411ac4b82bSMike Smith */ 1421ac4b82bSMike Smith static char *mlx_diagnose_command(struct mlx_command *mc); 1439eee27f1SMike Smith static void mlx_describe_controller(struct mlx_softc *sc); 144da8bb3a3SMike Smith static int mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2); 1451ac4b82bSMike Smith 1461ac4b82bSMike Smith /* 1471ac4b82bSMike Smith * Utility functions. 1481ac4b82bSMike Smith */ 1491ac4b82bSMike Smith static struct mlx_sysdrive *mlx_findunit(struct mlx_softc *sc, int unit); 1501ac4b82bSMike Smith 1511ac4b82bSMike Smith /******************************************************************************** 1521ac4b82bSMike Smith ******************************************************************************** 1531ac4b82bSMike Smith Public Interfaces 1541ac4b82bSMike Smith ******************************************************************************** 1551ac4b82bSMike Smith ********************************************************************************/ 1561ac4b82bSMike Smith 1571ac4b82bSMike Smith /******************************************************************************** 1581ac4b82bSMike Smith * Free all of the resources associated with (sc) 1591ac4b82bSMike Smith * 1601ac4b82bSMike Smith * Should not be called if the controller is active. 1611ac4b82bSMike Smith */ 1621ac4b82bSMike Smith void 1631ac4b82bSMike Smith mlx_free(struct mlx_softc *sc) 1641ac4b82bSMike Smith { 1651ac4b82bSMike Smith struct mlx_command *mc; 1661ac4b82bSMike Smith 167da8bb3a3SMike Smith debug_called(1); 1681ac4b82bSMike Smith 1691ac4b82bSMike Smith /* cancel status timeout */ 1701ac4b82bSMike Smith untimeout(mlx_periodic, sc, sc->mlx_timeout); 1711ac4b82bSMike Smith 1721ac4b82bSMike Smith /* throw away any command buffers */ 1731ac4b82bSMike Smith while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) { 1741ac4b82bSMike Smith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 1751ac4b82bSMike Smith mlx_freecmd(mc); 1761ac4b82bSMike Smith } 1771ac4b82bSMike Smith 1781ac4b82bSMike Smith /* destroy data-transfer DMA tag */ 1791ac4b82bSMike Smith if (sc->mlx_buffer_dmat) 1801ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_buffer_dmat); 1811ac4b82bSMike Smith 1821ac4b82bSMike Smith /* free and destroy DMA memory and tag for s/g lists */ 1831ac4b82bSMike Smith if (sc->mlx_sgtable) 1841ac4b82bSMike Smith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 1851ac4b82bSMike Smith if (sc->mlx_sg_dmat) 1861ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_sg_dmat); 1871ac4b82bSMike Smith 1881ac4b82bSMike Smith /* disconnect the interrupt handler */ 1891ac4b82bSMike Smith if (sc->mlx_intr) 1901ac4b82bSMike Smith bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr); 1911ac4b82bSMike Smith if (sc->mlx_irq != NULL) 1921ac4b82bSMike Smith bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq); 1931ac4b82bSMike Smith 1941ac4b82bSMike Smith /* destroy the parent DMA tag */ 1951ac4b82bSMike Smith if (sc->mlx_parent_dmat) 1961ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_parent_dmat); 1971ac4b82bSMike Smith 1981ac4b82bSMike Smith /* release the register window mapping */ 1991ac4b82bSMike Smith if (sc->mlx_mem != NULL) 2009b11c7baSMatthew N. Dodd bus_release_resource(sc->mlx_dev, sc->mlx_mem_type, sc->mlx_mem_rid, sc->mlx_mem); 2019eee27f1SMike Smith 2029eee27f1SMike Smith /* free controller enquiry data */ 2039eee27f1SMike Smith if (sc->mlx_enq2 != NULL) 2049eee27f1SMike Smith free(sc->mlx_enq2, M_DEVBUF); 205da8bb3a3SMike Smith 206da8bb3a3SMike Smith /* destroy control device */ 207da8bb3a3SMike Smith if (sc->mlx_dev_t != (dev_t)NULL) 208da8bb3a3SMike Smith destroy_dev(sc->mlx_dev_t); 2091ac4b82bSMike Smith } 2101ac4b82bSMike Smith 2111ac4b82bSMike Smith /******************************************************************************** 2121ac4b82bSMike Smith * Map the scatter/gather table into bus space 2131ac4b82bSMike Smith */ 2141ac4b82bSMike Smith static void 2151ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) 2161ac4b82bSMike Smith { 2171ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)arg; 2181ac4b82bSMike Smith 219da8bb3a3SMike Smith debug_called(1); 2201ac4b82bSMike Smith 2211ac4b82bSMike Smith /* save base of s/g table's address in bus space */ 2221ac4b82bSMike Smith sc->mlx_sgbusaddr = segs->ds_addr; 2231ac4b82bSMike Smith } 2241ac4b82bSMike Smith 2251ac4b82bSMike Smith static int 2261ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc) 2271ac4b82bSMike Smith { 2281ac4b82bSMike Smith size_t segsize; 229baff09dbSMike Smith int error, ncmd; 2301ac4b82bSMike Smith 231da8bb3a3SMike Smith debug_called(1); 2321ac4b82bSMike Smith 2331ac4b82bSMike Smith /* destroy any existing mappings */ 2341ac4b82bSMike Smith if (sc->mlx_sgtable) 2351ac4b82bSMike Smith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 2361ac4b82bSMike Smith if (sc->mlx_sg_dmat) 2371ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_sg_dmat); 2381ac4b82bSMike Smith 2391ac4b82bSMike Smith /* 2401ac4b82bSMike Smith * Create a single tag describing a region large enough to hold all of 241baff09dbSMike Smith * the s/g lists we will need. If we're called early on, we don't know how 242baff09dbSMike Smith * many commands we're going to be asked to support, so only allocate enough 243baff09dbSMike Smith * for a couple. 2441ac4b82bSMike Smith */ 245baff09dbSMike Smith if (sc->mlx_enq2 == NULL) { 246baff09dbSMike Smith ncmd = 2; 247baff09dbSMike Smith } else { 248baff09dbSMike Smith ncmd = sc->mlx_enq2->me_max_commands; 249baff09dbSMike Smith } 250baff09dbSMike Smith segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * ncmd; 2511ac4b82bSMike Smith error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 2521ac4b82bSMike Smith 1, 0, /* alignment,boundary */ 2531ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* lowaddr */ 2541ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* highaddr */ 2551ac4b82bSMike Smith NULL, NULL, /* filter, filterarg */ 2561ac4b82bSMike Smith segsize, 1, /* maxsize, nsegments */ 2571ac4b82bSMike Smith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 2581ac4b82bSMike Smith 0, /* flags */ 259fc3e87b3SScott Long NULL, NULL, /* lockfunc, lockarg */ 2601ac4b82bSMike Smith &sc->mlx_sg_dmat); 2611ac4b82bSMike Smith if (error != 0) { 2621ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n"); 2631ac4b82bSMike Smith return(ENOMEM); 2641ac4b82bSMike Smith } 2651ac4b82bSMike Smith 2661ac4b82bSMike Smith /* 2671ac4b82bSMike Smith * Allocate enough s/g maps for all commands and permanently map them into 2681ac4b82bSMike Smith * controller-visible space. 2691ac4b82bSMike Smith * 2701ac4b82bSMike Smith * XXX this assumes we can get enough space for all the s/g maps in one 271fc3e87b3SScott Long * contiguous slab. We may need to switch to a more complex arrangement 272fc3e87b3SScott Long * where we allocate in smaller chunks and keep a lookup table from slot 273fc3e87b3SScott Long * to bus address. 2741ac4b82bSMike Smith */ 275fc3e87b3SScott Long error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, 276fc3e87b3SScott Long BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap); 2771ac4b82bSMike Smith if (error) { 2781ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate s/g table\n"); 2791ac4b82bSMike Smith return(ENOMEM); 2801ac4b82bSMike Smith } 2811b4404f9SScott Long (void)bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, 282fc3e87b3SScott Long segsize, mlx_dma_map_sg, sc, 0); 2831ac4b82bSMike Smith return(0); 2841ac4b82bSMike Smith } 2851ac4b82bSMike Smith 2861ac4b82bSMike Smith /******************************************************************************** 2871ac4b82bSMike Smith * Initialise the controller and softc 2881ac4b82bSMike Smith */ 2891ac4b82bSMike Smith int 2901ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc) 2911ac4b82bSMike Smith { 292da8bb3a3SMike Smith struct mlx_enquiry_old *meo; 293da8bb3a3SMike Smith int rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg; 2941ac4b82bSMike Smith 295da8bb3a3SMike Smith debug_called(1); 2961ac4b82bSMike Smith 2971ac4b82bSMike Smith /* 2981ac4b82bSMike Smith * Initialise per-controller queues. 2991ac4b82bSMike Smith */ 3004b006d7bSMike Smith TAILQ_INIT(&sc->mlx_work); 3011ac4b82bSMike Smith TAILQ_INIT(&sc->mlx_freecmds); 30215fd5d22SMike Smith MLX_BIO_QINIT(sc->mlx_bioq); 3031ac4b82bSMike Smith 3041ac4b82bSMike Smith /* 3051ac4b82bSMike Smith * Select accessor methods based on controller interface type. 3061ac4b82bSMike Smith */ 3071ac4b82bSMike Smith switch(sc->mlx_iftype) { 308da8bb3a3SMike Smith case MLX_IFTYPE_2: 3091ac4b82bSMike Smith case MLX_IFTYPE_3: 3101ac4b82bSMike Smith sc->mlx_tryqueue = mlx_v3_tryqueue; 3111ac4b82bSMike Smith sc->mlx_findcomplete = mlx_v3_findcomplete; 3121ac4b82bSMike Smith sc->mlx_intaction = mlx_v3_intaction; 313da8bb3a3SMike Smith sc->mlx_fw_handshake = mlx_v3_fw_handshake; 3141ac4b82bSMike Smith break; 315f6b84b08SMike Smith case MLX_IFTYPE_4: 316f6b84b08SMike Smith sc->mlx_tryqueue = mlx_v4_tryqueue; 317f6b84b08SMike Smith sc->mlx_findcomplete = mlx_v4_findcomplete; 318f6b84b08SMike Smith sc->mlx_intaction = mlx_v4_intaction; 319da8bb3a3SMike Smith sc->mlx_fw_handshake = mlx_v4_fw_handshake; 320f6b84b08SMike Smith break; 3215792b7feSMike Smith case MLX_IFTYPE_5: 3225792b7feSMike Smith sc->mlx_tryqueue = mlx_v5_tryqueue; 3235792b7feSMike Smith sc->mlx_findcomplete = mlx_v5_findcomplete; 3245792b7feSMike Smith sc->mlx_intaction = mlx_v5_intaction; 325da8bb3a3SMike Smith sc->mlx_fw_handshake = mlx_v5_fw_handshake; 3265792b7feSMike Smith break; 3271ac4b82bSMike Smith default: 3285d278f5cSMike Smith mlx_free(sc); 3291ac4b82bSMike Smith return(ENXIO); /* should never happen */ 3301ac4b82bSMike Smith } 3311ac4b82bSMike Smith 3321ac4b82bSMike Smith /* disable interrupts before we start talking to the controller */ 3331ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 3341ac4b82bSMike Smith 3351ac4b82bSMike Smith /* 336da8bb3a3SMike Smith * Wait for the controller to come ready, handshake with the firmware if required. 337da8bb3a3SMike Smith * This is typically only necessary on platforms where the controller BIOS does not 338da8bb3a3SMike Smith * run. 339da8bb3a3SMike Smith */ 340da8bb3a3SMike Smith hsmsg = 0; 341da8bb3a3SMike Smith DELAY(1000); 342da8bb3a3SMike Smith while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) { 343da8bb3a3SMike Smith /* report first time around... */ 344da8bb3a3SMike Smith if (hsmsg == 0) { 345da8bb3a3SMike Smith device_printf(sc->mlx_dev, "controller initialisation in progress...\n"); 346da8bb3a3SMike Smith hsmsg = 1; 347da8bb3a3SMike Smith } 348da8bb3a3SMike Smith /* did we get a real message? */ 349da8bb3a3SMike Smith if (hscode == 2) { 350da8bb3a3SMike Smith hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2); 351da8bb3a3SMike Smith /* fatal initialisation error? */ 352da8bb3a3SMike Smith if (hscode != 0) { 353da8bb3a3SMike Smith mlx_free(sc); 354da8bb3a3SMike Smith return(ENXIO); 355da8bb3a3SMike Smith } 356da8bb3a3SMike Smith } 357da8bb3a3SMike Smith } 358da8bb3a3SMike Smith if (hsmsg == 1) 359da8bb3a3SMike Smith device_printf(sc->mlx_dev, "initialisation complete.\n"); 360da8bb3a3SMike Smith 361da8bb3a3SMike Smith /* 3621ac4b82bSMike Smith * Allocate and connect our interrupt. 3631ac4b82bSMike Smith */ 3641ac4b82bSMike Smith rid = 0; 3655f96beb9SNate Lawson sc->mlx_irq = bus_alloc_resource_any(sc->mlx_dev, SYS_RES_IRQ, &rid, 3665f96beb9SNate Lawson RF_SHAREABLE | RF_ACTIVE); 3671ac4b82bSMike Smith if (sc->mlx_irq == NULL) { 368421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't allocate interrupt\n"); 3691ac4b82bSMike Smith mlx_free(sc); 3701ac4b82bSMike Smith return(ENXIO); 3711ac4b82bSMike Smith } 372ed34d0adSMark Murray error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO | INTR_ENTROPY, mlx_intr, sc, &sc->mlx_intr); 3731ac4b82bSMike Smith if (error) { 374421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't set up interrupt\n"); 3751ac4b82bSMike Smith mlx_free(sc); 3761ac4b82bSMike Smith return(ENXIO); 3771ac4b82bSMike Smith } 3781ac4b82bSMike Smith 3791ac4b82bSMike Smith /* 3801ac4b82bSMike Smith * Create DMA tag for mapping buffers into controller-addressable space. 3811ac4b82bSMike Smith */ 3821ac4b82bSMike Smith error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 3831b4404f9SScott Long 1, 0, /* align, boundary */ 3841ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* lowaddr */ 3851ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* highaddr */ 3861ac4b82bSMike Smith NULL, NULL, /* filter, filterarg */ 387baff09dbSMike Smith MAXBSIZE, MLX_NSEG, /* maxsize, nsegments */ 3881ac4b82bSMike Smith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 3891ac4b82bSMike Smith 0, /* flags */ 390f6b1c44dSScott Long busdma_lock_mutex, /* lockfunc */ 391f6b1c44dSScott Long &Giant, /* lockarg */ 3921ac4b82bSMike Smith &sc->mlx_buffer_dmat); 3931ac4b82bSMike Smith if (error != 0) { 3941ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n"); 3955d278f5cSMike Smith mlx_free(sc); 3961ac4b82bSMike Smith return(ENOMEM); 3971ac4b82bSMike Smith } 3981ac4b82bSMike Smith 3991ac4b82bSMike Smith /* 4001b4404f9SScott Long * Create some initial scatter/gather mappings so we can run the probe 4011b4404f9SScott Long * commands. 4021ac4b82bSMike Smith */ 4031ac4b82bSMike Smith error = mlx_sglist_map(sc); 4041ac4b82bSMike Smith if (error != 0) { 405421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n"); 4065d278f5cSMike Smith mlx_free(sc); 4071ac4b82bSMike Smith return(error); 4081ac4b82bSMike Smith } 4091ac4b82bSMike Smith 410baff09dbSMike Smith /* 411baff09dbSMike Smith * We don't (yet) know where the event log is up to. 412baff09dbSMike Smith */ 413baff09dbSMike Smith sc->mlx_currevent = -1; 414baff09dbSMike Smith 415baff09dbSMike Smith /* 416baff09dbSMike Smith * Obtain controller feature information 417baff09dbSMike Smith */ 4189eee27f1SMike Smith if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) { 4191ac4b82bSMike Smith device_printf(sc->mlx_dev, "ENQUIRY2 failed\n"); 4205d278f5cSMike Smith mlx_free(sc); 4211ac4b82bSMike Smith return(ENXIO); 4221ac4b82bSMike Smith } 4231ac4b82bSMike Smith 4249eee27f1SMike Smith /* 4251ac4b82bSMike Smith * Do quirk/feature related things. 4261ac4b82bSMike Smith */ 4279eee27f1SMike Smith fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff; 4281ac4b82bSMike Smith switch(sc->mlx_iftype) { 429da8bb3a3SMike Smith case MLX_IFTYPE_2: 430da8bb3a3SMike Smith /* These controllers don't report the firmware version in the ENQUIRY2 response */ 431da8bb3a3SMike Smith if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) { 432da8bb3a3SMike Smith device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n"); 4335d278f5cSMike Smith mlx_free(sc); 434da8bb3a3SMike Smith return(ENXIO); 435da8bb3a3SMike Smith } 436da8bb3a3SMike Smith sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor; 437da8bb3a3SMike Smith 438da8bb3a3SMike Smith /* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */ 439da8bb3a3SMike Smith if (meo->me_fwminor < 42) { 440da8bb3a3SMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 441da8bb3a3SMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n"); 442da8bb3a3SMike Smith } 443caa32ef5SColin Percival free(meo, M_DEVBUF); 444da8bb3a3SMike Smith break; 4451ac4b82bSMike Smith case MLX_IFTYPE_3: 446f6b84b08SMike Smith /* XXX certify 3.52? */ 4479eee27f1SMike Smith if (fwminor < 51) { 4484b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4494b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n"); 4501ac4b82bSMike Smith } 4511ac4b82bSMike Smith break; 452f6b84b08SMike Smith case MLX_IFTYPE_4: 453f6b84b08SMike Smith /* XXX certify firmware versions? */ 4549eee27f1SMike Smith if (fwminor < 6) { 4554b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4564b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n"); 457f6b84b08SMike Smith } 458f6b84b08SMike Smith break; 4595792b7feSMike Smith case MLX_IFTYPE_5: 4609eee27f1SMike Smith if (fwminor < 7) { 4615792b7feSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4625792b7feSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n"); 4635792b7feSMike Smith } 4645792b7feSMike Smith break; 4651ac4b82bSMike Smith default: 4665d278f5cSMike Smith mlx_free(sc); 4671ac4b82bSMike Smith return(ENXIO); /* should never happen */ 4681ac4b82bSMike Smith } 4691ac4b82bSMike Smith 4701ac4b82bSMike Smith /* 471baff09dbSMike Smith * Create the final scatter/gather mappings now that we have characterised the controller. 4721ac4b82bSMike Smith */ 4731ac4b82bSMike Smith error = mlx_sglist_map(sc); 4741ac4b82bSMike Smith if (error != 0) { 475baff09dbSMike Smith device_printf(sc->mlx_dev, "can't make final s/g list mapping\n"); 4765d278f5cSMike Smith mlx_free(sc); 4771ac4b82bSMike Smith return(error); 4781ac4b82bSMike Smith } 4791ac4b82bSMike Smith 4801ac4b82bSMike Smith /* 481421f2f7dSMike Smith * No user-requested background operation is in progress. 4821ac4b82bSMike Smith */ 483421f2f7dSMike Smith sc->mlx_background = 0; 484421f2f7dSMike Smith sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; 4851ac4b82bSMike Smith 4861ac4b82bSMike Smith /* 487da8bb3a3SMike Smith * Create the control device. 4881ac4b82bSMike Smith */ 489da8bb3a3SMike Smith sc->mlx_dev_t = make_dev(&mlx_cdevsw, device_get_unit(sc->mlx_dev), UID_ROOT, GID_OPERATOR, 490da8bb3a3SMike Smith S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev)); 4911ac4b82bSMike Smith 4921ac4b82bSMike Smith /* 4931ac4b82bSMike Smith * Start the timeout routine. 4941ac4b82bSMike Smith */ 4951ac4b82bSMike Smith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 4961ac4b82bSMike Smith 497da8bb3a3SMike Smith /* print a little information about the controller */ 498da8bb3a3SMike Smith mlx_describe_controller(sc); 499da8bb3a3SMike Smith 5001ac4b82bSMike Smith return(0); 5011ac4b82bSMike Smith } 5021ac4b82bSMike Smith 5031ac4b82bSMike Smith /******************************************************************************** 5041ac4b82bSMike Smith * Locate disk resources and attach children to them. 5051ac4b82bSMike Smith */ 5061ac4b82bSMike Smith void 5071ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc) 5081ac4b82bSMike Smith { 5091ac4b82bSMike Smith struct mlx_enq_sys_drive *mes; 5101ac4b82bSMike Smith struct mlx_sysdrive *dr; 5111ac4b82bSMike Smith int i, error; 5121ac4b82bSMike Smith 513da8bb3a3SMike Smith debug_called(1); 5141ac4b82bSMike Smith 5151ac4b82bSMike Smith /* 5161ac4b82bSMike Smith * Scan all the system drives and attach children for those that 5171ac4b82bSMike Smith * don't currently have them. 5181ac4b82bSMike Smith */ 5191ac4b82bSMike Smith mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL); 5201ac4b82bSMike Smith if (mes == NULL) { 5219eee27f1SMike Smith device_printf(sc->mlx_dev, "error fetching drive status\n"); 5221ac4b82bSMike Smith return; 5231ac4b82bSMike Smith } 5241ac4b82bSMike Smith 5251ac4b82bSMike Smith /* iterate over drives returned */ 5261ac4b82bSMike Smith for (i = 0, dr = &sc->mlx_sysdrive[0]; 5271ac4b82bSMike Smith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 5281ac4b82bSMike Smith i++, dr++) { 5291ac4b82bSMike Smith /* are we already attached to this drive? */ 5301ac4b82bSMike Smith if (dr->ms_disk == 0) { 5311ac4b82bSMike Smith /* pick up drive information */ 5321ac4b82bSMike Smith dr->ms_size = mes[i].sd_size; 533f6b84b08SMike Smith dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf; 5341ac4b82bSMike Smith dr->ms_state = mes[i].sd_state; 5351ac4b82bSMike Smith 5361ac4b82bSMike Smith /* generate geometry information */ 5371ac4b82bSMike Smith if (sc->mlx_geom == MLX_GEOM_128_32) { 5381ac4b82bSMike Smith dr->ms_heads = 128; 5391ac4b82bSMike Smith dr->ms_sectors = 32; 5401ac4b82bSMike Smith dr->ms_cylinders = dr->ms_size / (128 * 32); 5411ac4b82bSMike Smith } else { /* MLX_GEOM_255/63 */ 5421ac4b82bSMike Smith dr->ms_heads = 255; 5431ac4b82bSMike Smith dr->ms_sectors = 63; 5441ac4b82bSMike Smith dr->ms_cylinders = dr->ms_size / (255 * 63); 5451ac4b82bSMike Smith } 546fe0d4089SMatthew N. Dodd dr->ms_disk = device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1); 5471ac4b82bSMike Smith if (dr->ms_disk == 0) 5481ac4b82bSMike Smith device_printf(sc->mlx_dev, "device_add_child failed\n"); 549fe0d4089SMatthew N. Dodd device_set_ivars(dr->ms_disk, dr); 5501ac4b82bSMike Smith } 5511ac4b82bSMike Smith } 5521ac4b82bSMike Smith free(mes, M_DEVBUF); 5531ac4b82bSMike Smith if ((error = bus_generic_attach(sc->mlx_dev)) != 0) 5541ac4b82bSMike Smith device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error); 5551ac4b82bSMike Smith 5561ac4b82bSMike Smith /* mark controller back up */ 5571ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_SHUTDOWN; 5581ac4b82bSMike Smith 5591ac4b82bSMike Smith /* enable interrupts */ 5601ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 5611ac4b82bSMike Smith } 5621ac4b82bSMike Smith 5631ac4b82bSMike Smith /******************************************************************************** 5641ac4b82bSMike Smith * Disconnect from the controller completely, in preparation for unload. 5651ac4b82bSMike Smith */ 5661ac4b82bSMike Smith int 5671ac4b82bSMike Smith mlx_detach(device_t dev) 5681ac4b82bSMike Smith { 5691ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 5705792b7feSMike Smith struct mlxd_softc *mlxd; 5715792b7feSMike Smith int i, s, error; 5721ac4b82bSMike Smith 573da8bb3a3SMike Smith debug_called(1); 5741ac4b82bSMike Smith 5755792b7feSMike Smith error = EBUSY; 5765792b7feSMike Smith s = splbio(); 5771ac4b82bSMike Smith if (sc->mlx_state & MLX_STATE_OPEN) 5785792b7feSMike Smith goto out; 5791ac4b82bSMike Smith 5805792b7feSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 5815792b7feSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 5825792b7feSMike Smith mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk); 5835792b7feSMike Smith if (mlxd->mlxd_flags & MLXD_OPEN) { /* drive is mounted, abort detach */ 5845792b7feSMike Smith device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n"); 5855792b7feSMike Smith goto out; 5865792b7feSMike Smith } 5875792b7feSMike Smith } 5885792b7feSMike Smith } 5891ac4b82bSMike Smith if ((error = mlx_shutdown(dev))) 5905792b7feSMike Smith goto out; 5911ac4b82bSMike Smith 5921ac4b82bSMike Smith mlx_free(sc); 5931ac4b82bSMike Smith 5945792b7feSMike Smith error = 0; 5955792b7feSMike Smith out: 5965792b7feSMike Smith splx(s); 5975792b7feSMike Smith return(error); 5981ac4b82bSMike Smith } 5991ac4b82bSMike Smith 6001ac4b82bSMike Smith /******************************************************************************** 6011ac4b82bSMike Smith * Bring the controller down to a dormant state and detach all child devices. 6021ac4b82bSMike Smith * 6031ac4b82bSMike Smith * This function is called before detach, system shutdown, or before performing 6041ac4b82bSMike Smith * an operation which may add or delete system disks. (Call mlx_startup to 6051ac4b82bSMike Smith * resume normal operation.) 6061ac4b82bSMike Smith * 6078177437dSPoul-Henning Kamp * Note that we can assume that the bioq on the controller is empty, as we won't 6081ac4b82bSMike Smith * allow shutdown if any device is open. 6091ac4b82bSMike Smith */ 6101ac4b82bSMike Smith int 6111ac4b82bSMike Smith mlx_shutdown(device_t dev) 6121ac4b82bSMike Smith { 6131ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 6141ac4b82bSMike Smith int i, s, error; 6151ac4b82bSMike Smith 616da8bb3a3SMike Smith debug_called(1); 6171ac4b82bSMike Smith 6181ac4b82bSMike Smith s = splbio(); 6191ac4b82bSMike Smith error = 0; 6201ac4b82bSMike Smith 6211ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_SHUTDOWN; 6225792b7feSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 6231ac4b82bSMike Smith 6241ac4b82bSMike Smith /* flush controller */ 6251ac4b82bSMike Smith device_printf(sc->mlx_dev, "flushing cache..."); 6261ac4b82bSMike Smith if (mlx_flush(sc)) { 6271ac4b82bSMike Smith printf("failed\n"); 6281ac4b82bSMike Smith } else { 6291ac4b82bSMike Smith printf("done\n"); 6301ac4b82bSMike Smith } 6311ac4b82bSMike Smith 6321ac4b82bSMike Smith /* delete all our child devices */ 6331ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 6341ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 6351ac4b82bSMike Smith if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0) 6361ac4b82bSMike Smith goto out; 6371ac4b82bSMike Smith sc->mlx_sysdrive[i].ms_disk = 0; 6381ac4b82bSMike Smith } 6391ac4b82bSMike Smith } 6401ac4b82bSMike Smith 6411ac4b82bSMike Smith out: 6421ac4b82bSMike Smith splx(s); 6431ac4b82bSMike Smith return(error); 6441ac4b82bSMike Smith } 6451ac4b82bSMike Smith 6461ac4b82bSMike Smith /******************************************************************************** 6471ac4b82bSMike Smith * Bring the controller to a quiescent state, ready for system suspend. 6481ac4b82bSMike Smith */ 6491ac4b82bSMike Smith int 6501ac4b82bSMike Smith mlx_suspend(device_t dev) 6511ac4b82bSMike Smith { 6521ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 6531ac4b82bSMike Smith int s; 6541ac4b82bSMike Smith 655da8bb3a3SMike Smith debug_called(1); 6561ac4b82bSMike Smith 6571ac4b82bSMike Smith s = splbio(); 6581ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_SUSPEND; 6591ac4b82bSMike Smith 6601ac4b82bSMike Smith /* flush controller */ 6611ac4b82bSMike Smith device_printf(sc->mlx_dev, "flushing cache..."); 6621ac4b82bSMike Smith printf("%s\n", mlx_flush(sc) ? "failed" : "done"); 6631ac4b82bSMike Smith 6641ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 6651ac4b82bSMike Smith splx(s); 6661ac4b82bSMike Smith 6671ac4b82bSMike Smith return(0); 6681ac4b82bSMike Smith } 6691ac4b82bSMike Smith 6701ac4b82bSMike Smith /******************************************************************************** 6711ac4b82bSMike Smith * Bring the controller back to a state ready for operation. 6721ac4b82bSMike Smith */ 6731ac4b82bSMike Smith int 6741ac4b82bSMike Smith mlx_resume(device_t dev) 6751ac4b82bSMike Smith { 6761ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 6771ac4b82bSMike Smith 678da8bb3a3SMike Smith debug_called(1); 6791ac4b82bSMike Smith 6801ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_SUSPEND; 6811ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 6821ac4b82bSMike Smith 6831ac4b82bSMike Smith return(0); 6841ac4b82bSMike Smith } 6851ac4b82bSMike Smith 6861ac4b82bSMike Smith /******************************************************************************* 6871ac4b82bSMike Smith * Take an interrupt, or be poked by other code to look for interrupt-worthy 6881ac4b82bSMike Smith * status. 6891ac4b82bSMike Smith */ 6901ac4b82bSMike Smith void 6911ac4b82bSMike Smith mlx_intr(void *arg) 6921ac4b82bSMike Smith { 6931ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)arg; 6941ac4b82bSMike Smith 695da8bb3a3SMike Smith debug_called(1); 6961ac4b82bSMike Smith 6975792b7feSMike Smith /* collect finished commands, queue anything waiting */ 6985792b7feSMike Smith mlx_done(sc); 6991ac4b82bSMike Smith }; 7001ac4b82bSMike Smith 7011ac4b82bSMike Smith /******************************************************************************* 7021ac4b82bSMike Smith * Receive a buf structure from a child device and queue it on a particular 7031ac4b82bSMike Smith * disk resource, then poke the disk resource to start as much work as it can. 7041ac4b82bSMike Smith */ 7051ac4b82bSMike Smith int 70615fd5d22SMike Smith mlx_submit_buf(struct mlx_softc *sc, mlx_bio *bp) 7071ac4b82bSMike Smith { 7084b006d7bSMike Smith int s; 7094b006d7bSMike Smith 710da8bb3a3SMike Smith debug_called(1); 7111ac4b82bSMike Smith 7124b006d7bSMike Smith s = splbio(); 71315fd5d22SMike Smith MLX_BIO_QINSERT(sc->mlx_bioq, bp); 7141ac4b82bSMike Smith sc->mlx_waitbufs++; 7154b006d7bSMike Smith splx(s); 7161ac4b82bSMike Smith mlx_startio(sc); 7171ac4b82bSMike Smith return(0); 7181ac4b82bSMike Smith } 7191ac4b82bSMike Smith 7201ac4b82bSMike Smith /******************************************************************************** 7211ac4b82bSMike Smith * Accept an open operation on the control device. 7221ac4b82bSMike Smith */ 7231ac4b82bSMike Smith int 724b40ce416SJulian Elischer mlx_open(dev_t dev, int flags, int fmt, struct thread *td) 7251ac4b82bSMike Smith { 7261ac4b82bSMike Smith int unit = minor(dev); 7271ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 7281ac4b82bSMike Smith 7291ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_OPEN; 7301ac4b82bSMike Smith return(0); 7311ac4b82bSMike Smith } 7321ac4b82bSMike Smith 7331ac4b82bSMike Smith /******************************************************************************** 7341ac4b82bSMike Smith * Accept the last close on the control device. 7351ac4b82bSMike Smith */ 7361ac4b82bSMike Smith int 737b40ce416SJulian Elischer mlx_close(dev_t dev, int flags, int fmt, struct thread *td) 7381ac4b82bSMike Smith { 7391ac4b82bSMike Smith int unit = minor(dev); 7401ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 7411ac4b82bSMike Smith 7421ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_OPEN; 7431ac4b82bSMike Smith return (0); 7441ac4b82bSMike Smith } 7451ac4b82bSMike Smith 7461ac4b82bSMike Smith /******************************************************************************** 7471ac4b82bSMike Smith * Handle controller-specific control operations. 7481ac4b82bSMike Smith */ 7491ac4b82bSMike Smith int 750b40ce416SJulian Elischer mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) 7511ac4b82bSMike Smith { 7521ac4b82bSMike Smith int unit = minor(dev); 7531ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 754421f2f7dSMike Smith struct mlx_rebuild_request *rb = (struct mlx_rebuild_request *)addr; 755421f2f7dSMike Smith struct mlx_rebuild_status *rs = (struct mlx_rebuild_status *)addr; 7561ac4b82bSMike Smith int *arg = (int *)addr; 7571ac4b82bSMike Smith struct mlx_pause *mp; 7581ac4b82bSMike Smith struct mlx_sysdrive *dr; 7591ac4b82bSMike Smith struct mlxd_softc *mlxd; 7601ac4b82bSMike Smith int i, error; 7611ac4b82bSMike Smith 7621ac4b82bSMike Smith switch(cmd) { 7631ac4b82bSMike Smith /* 7641ac4b82bSMike Smith * Enumerate connected system drives; returns the first system drive's 7651ac4b82bSMike Smith * unit number if *arg is -1, or the next unit after *arg if it's 7661ac4b82bSMike Smith * a valid unit on this controller. 7671ac4b82bSMike Smith */ 7681ac4b82bSMike Smith case MLX_NEXT_CHILD: 7691ac4b82bSMike Smith /* search system drives */ 7701ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 7711ac4b82bSMike Smith /* is this one attached? */ 7721ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 7731ac4b82bSMike Smith /* looking for the next one we come across? */ 7741ac4b82bSMike Smith if (*arg == -1) { 775421f2f7dSMike Smith *arg = device_get_unit(sc->mlx_sysdrive[0].ms_disk); 7761ac4b82bSMike Smith return(0); 7771ac4b82bSMike Smith } 7781ac4b82bSMike Smith /* we want the one after this one */ 7791ac4b82bSMike Smith if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 7801ac4b82bSMike Smith *arg = -1; 7811ac4b82bSMike Smith } 7821ac4b82bSMike Smith } 7831ac4b82bSMike Smith return(ENOENT); 7841ac4b82bSMike Smith 7851ac4b82bSMike Smith /* 7861ac4b82bSMike Smith * Scan the controller to see whether new drives have appeared. 7871ac4b82bSMike Smith */ 7881ac4b82bSMike Smith case MLX_RESCAN_DRIVES: 7891ac4b82bSMike Smith mlx_startup(sc); 7901ac4b82bSMike Smith return(0); 7911ac4b82bSMike Smith 7921ac4b82bSMike Smith /* 7931ac4b82bSMike Smith * Disconnect from the specified drive; it may be about to go 7941ac4b82bSMike Smith * away. 7951ac4b82bSMike Smith */ 7961ac4b82bSMike Smith case MLX_DETACH_DRIVE: /* detach one drive */ 7971ac4b82bSMike Smith 7981ac4b82bSMike Smith if (((dr = mlx_findunit(sc, *arg)) == NULL) || 7991ac4b82bSMike Smith ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) 8001ac4b82bSMike Smith return(ENOENT); 8011ac4b82bSMike Smith 8021ac4b82bSMike Smith device_printf(dr->ms_disk, "detaching..."); 8031ac4b82bSMike Smith error = 0; 8041ac4b82bSMike Smith if (mlxd->mlxd_flags & MLXD_OPEN) { 8051ac4b82bSMike Smith error = EBUSY; 8061ac4b82bSMike Smith goto detach_out; 8071ac4b82bSMike Smith } 8081ac4b82bSMike Smith 8091ac4b82bSMike Smith /* flush controller */ 8101ac4b82bSMike Smith if (mlx_flush(sc)) { 8111ac4b82bSMike Smith error = EBUSY; 8121ac4b82bSMike Smith goto detach_out; 8131ac4b82bSMike Smith } 8141ac4b82bSMike Smith 8151ac4b82bSMike Smith /* nuke drive */ 8161ac4b82bSMike Smith if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0) 8171ac4b82bSMike Smith goto detach_out; 8181ac4b82bSMike Smith dr->ms_disk = 0; 8191ac4b82bSMike Smith 8201ac4b82bSMike Smith detach_out: 8211ac4b82bSMike Smith if (error) { 8221ac4b82bSMike Smith printf("failed\n"); 8231ac4b82bSMike Smith } else { 8241ac4b82bSMike Smith printf("done\n"); 8251ac4b82bSMike Smith } 8261ac4b82bSMike Smith return(error); 8271ac4b82bSMike Smith 8281ac4b82bSMike Smith /* 8291ac4b82bSMike Smith * Pause one or more SCSI channels for a period of time, to assist 8301ac4b82bSMike Smith * in the process of hot-swapping devices. 8311ac4b82bSMike Smith * 8321ac4b82bSMike Smith * Note that at least the 3.51 firmware on the DAC960PL doesn't seem 8331ac4b82bSMike Smith * to do this right. 8341ac4b82bSMike Smith */ 8351ac4b82bSMike Smith case MLX_PAUSE_CHANNEL: /* schedule a channel pause */ 8361ac4b82bSMike Smith /* Does this command work on this firmware? */ 8371ac4b82bSMike Smith if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS)) 8381ac4b82bSMike Smith return(EOPNOTSUPP); 8391ac4b82bSMike Smith 8401ac4b82bSMike Smith mp = (struct mlx_pause *)addr; 8411ac4b82bSMike Smith if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) { 8421ac4b82bSMike Smith /* cancel a pending pause operation */ 8431ac4b82bSMike Smith sc->mlx_pause.mp_which = 0; 8441ac4b82bSMike Smith } else { 8451ac4b82bSMike Smith /* fix for legal channels */ 8469eee27f1SMike Smith mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1); 8471ac4b82bSMike Smith /* check time values */ 8481ac4b82bSMike Smith if ((mp->mp_when < 0) || (mp->mp_when > 3600)) 8491ac4b82bSMike Smith return(EINVAL); 8501ac4b82bSMike Smith if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30))) 8511ac4b82bSMike Smith return(EINVAL); 8521ac4b82bSMike Smith 8531ac4b82bSMike Smith /* check for a pause currently running */ 8541ac4b82bSMike Smith if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) 8551ac4b82bSMike Smith return(EBUSY); 8561ac4b82bSMike Smith 8571ac4b82bSMike Smith /* looks ok, go with it */ 8581ac4b82bSMike Smith sc->mlx_pause.mp_which = mp->mp_which; 8591ac4b82bSMike Smith sc->mlx_pause.mp_when = time_second + mp->mp_when; 8601ac4b82bSMike Smith sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong; 8611ac4b82bSMike Smith } 8621ac4b82bSMike Smith return(0); 8631ac4b82bSMike Smith 8641ac4b82bSMike Smith /* 8651ac4b82bSMike Smith * Accept a command passthrough-style. 8661ac4b82bSMike Smith */ 8671ac4b82bSMike Smith case MLX_COMMAND: 8681ac4b82bSMike Smith return(mlx_user_command(sc, (struct mlx_usercommand *)addr)); 8691ac4b82bSMike Smith 870421f2f7dSMike Smith /* 871421f2f7dSMike Smith * Start a rebuild on a given SCSI disk 872421f2f7dSMike Smith */ 873421f2f7dSMike Smith case MLX_REBUILDASYNC: 874421f2f7dSMike Smith if (sc->mlx_background != 0) { 875421f2f7dSMike Smith rb->rr_status = 0x0106; 876421f2f7dSMike Smith return(EBUSY); 877421f2f7dSMike Smith } 878421f2f7dSMike Smith rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target); 879421f2f7dSMike Smith switch (rb->rr_status) { 880421f2f7dSMike Smith case 0: 881421f2f7dSMike Smith error = 0; 882421f2f7dSMike Smith break; 883421f2f7dSMike Smith case 0x10000: 884421f2f7dSMike Smith error = ENOMEM; /* couldn't set up the command */ 885421f2f7dSMike Smith break; 886421f2f7dSMike Smith case 0x0002: 887421f2f7dSMike Smith error = EBUSY; 888421f2f7dSMike Smith break; 889421f2f7dSMike Smith case 0x0104: 890421f2f7dSMike Smith error = EIO; 891421f2f7dSMike Smith break; 892421f2f7dSMike Smith case 0x0105: 893421f2f7dSMike Smith error = ERANGE; 894421f2f7dSMike Smith break; 895421f2f7dSMike Smith case 0x0106: 896421f2f7dSMike Smith error = EBUSY; 897421f2f7dSMike Smith break; 898421f2f7dSMike Smith default: 899421f2f7dSMike Smith error = EINVAL; 900421f2f7dSMike Smith break; 901421f2f7dSMike Smith } 902421f2f7dSMike Smith if (error == 0) 903421f2f7dSMike Smith sc->mlx_background = MLX_BACKGROUND_REBUILD; 904421f2f7dSMike Smith return(error); 905421f2f7dSMike Smith 906421f2f7dSMike Smith /* 907421f2f7dSMike Smith * Get the status of the current rebuild or consistency check. 908421f2f7dSMike Smith */ 909421f2f7dSMike Smith case MLX_REBUILDSTAT: 910421f2f7dSMike Smith *rs = sc->mlx_rebuildstat; 911421f2f7dSMike Smith return(0); 912421f2f7dSMike Smith 913421f2f7dSMike Smith /* 914421f2f7dSMike Smith * Return the per-controller system drive number matching the 915421f2f7dSMike Smith * disk device number in (arg), if it happens to belong to us. 916421f2f7dSMike Smith */ 917421f2f7dSMike Smith case MLX_GET_SYSDRIVE: 918421f2f7dSMike Smith error = ENOENT; 919421f2f7dSMike Smith mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg); 920421f2f7dSMike Smith if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) && 921421f2f7dSMike Smith (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) { 922421f2f7dSMike Smith error = 0; 923421f2f7dSMike Smith *arg = mlxd->mlxd_drive - sc->mlx_sysdrive; 924421f2f7dSMike Smith } 925421f2f7dSMike Smith return(error); 926421f2f7dSMike Smith 9271ac4b82bSMike Smith default: 9281ac4b82bSMike Smith return(ENOTTY); 9291ac4b82bSMike Smith } 9301ac4b82bSMike Smith } 9311ac4b82bSMike Smith 9321ac4b82bSMike Smith /******************************************************************************** 9331ac4b82bSMike Smith * Handle operations requested by a System Drive connected to this controller. 9341ac4b82bSMike Smith */ 9351ac4b82bSMike Smith int 9361ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd, 937b40ce416SJulian Elischer caddr_t addr, int32_t flag, struct thread *td) 9381ac4b82bSMike Smith { 9391ac4b82bSMike Smith int *arg = (int *)addr; 940421f2f7dSMike Smith int error, result; 9411ac4b82bSMike Smith 9421ac4b82bSMike Smith switch(cmd) { 9431ac4b82bSMike Smith /* 9441ac4b82bSMike Smith * Return the current status of this drive. 9451ac4b82bSMike Smith */ 9461ac4b82bSMike Smith case MLXD_STATUS: 9471ac4b82bSMike Smith *arg = drive->ms_state; 9481ac4b82bSMike Smith return(0); 9491ac4b82bSMike Smith 9501ac4b82bSMike Smith /* 951421f2f7dSMike Smith * Start a background consistency check on this drive. 9521ac4b82bSMike Smith */ 953421f2f7dSMike Smith case MLXD_CHECKASYNC: /* start a background consistency check */ 954421f2f7dSMike Smith if (sc->mlx_background != 0) { 955421f2f7dSMike Smith *arg = 0x0106; 9561ac4b82bSMike Smith return(EBUSY); 957421f2f7dSMike Smith } 958421f2f7dSMike Smith result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]); 959421f2f7dSMike Smith switch (result) { 9601ac4b82bSMike Smith case 0: 9611ac4b82bSMike Smith error = 0; 9621ac4b82bSMike Smith break; 9631ac4b82bSMike Smith case 0x10000: 9641ac4b82bSMike Smith error = ENOMEM; /* couldn't set up the command */ 9651ac4b82bSMike Smith break; 9661ac4b82bSMike Smith case 0x0002: 9671ac4b82bSMike Smith error = EIO; 9681ac4b82bSMike Smith break; 9691ac4b82bSMike Smith case 0x0105: 9701ac4b82bSMike Smith error = ERANGE; 9711ac4b82bSMike Smith break; 972421f2f7dSMike Smith case 0x0106: 973421f2f7dSMike Smith error = EBUSY; 974421f2f7dSMike Smith break; 9751ac4b82bSMike Smith default: 9761ac4b82bSMike Smith error = EINVAL; 9771ac4b82bSMike Smith break; 9781ac4b82bSMike Smith } 979421f2f7dSMike Smith if (error == 0) 980421f2f7dSMike Smith sc->mlx_background = MLX_BACKGROUND_CHECK; 981421f2f7dSMike Smith *arg = result; 9821ac4b82bSMike Smith return(error); 9831ac4b82bSMike Smith 9841ac4b82bSMike Smith } 9851ac4b82bSMike Smith return(ENOIOCTL); 9861ac4b82bSMike Smith } 9871ac4b82bSMike Smith 9881ac4b82bSMike Smith 9891ac4b82bSMike Smith /******************************************************************************** 9901ac4b82bSMike Smith ******************************************************************************** 9911ac4b82bSMike Smith Status Monitoring 9921ac4b82bSMike Smith ******************************************************************************** 9931ac4b82bSMike Smith ********************************************************************************/ 9941ac4b82bSMike Smith 9951ac4b82bSMike Smith /******************************************************************************** 9961ac4b82bSMike Smith * Fire off commands to periodically check the status of connected drives. 9971ac4b82bSMike Smith */ 9981ac4b82bSMike Smith static void 9991ac4b82bSMike Smith mlx_periodic(void *data) 10001ac4b82bSMike Smith { 10011ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)data; 10021ac4b82bSMike Smith 1003da8bb3a3SMike Smith debug_called(1); 10041ac4b82bSMike Smith 10051ac4b82bSMike Smith /* 10061ac4b82bSMike Smith * Run a bus pause? 10071ac4b82bSMike Smith */ 10081ac4b82bSMike Smith if ((sc->mlx_pause.mp_which != 0) && 10091ac4b82bSMike Smith (sc->mlx_pause.mp_when > 0) && 10101ac4b82bSMike Smith (time_second >= sc->mlx_pause.mp_when)){ 10111ac4b82bSMike Smith 10121ac4b82bSMike Smith mlx_pause_action(sc); /* pause is running */ 10131ac4b82bSMike Smith sc->mlx_pause.mp_when = 0; 10141ac4b82bSMike Smith sysbeep(500, hz); 10151ac4b82bSMike Smith 10161ac4b82bSMike Smith /* 10171ac4b82bSMike Smith * Bus pause still running? 10181ac4b82bSMike Smith */ 10191ac4b82bSMike Smith } else if ((sc->mlx_pause.mp_which != 0) && 10201ac4b82bSMike Smith (sc->mlx_pause.mp_when == 0)) { 10211ac4b82bSMike Smith 10221ac4b82bSMike Smith /* time to stop bus pause? */ 10231ac4b82bSMike Smith if (time_second >= sc->mlx_pause.mp_howlong) { 10241ac4b82bSMike Smith mlx_pause_action(sc); 10251ac4b82bSMike Smith sc->mlx_pause.mp_which = 0; /* pause is complete */ 10261ac4b82bSMike Smith sysbeep(500, hz); 10271ac4b82bSMike Smith } else { 10281ac4b82bSMike Smith sysbeep((time_second % 5) * 100 + 500, hz/8); 10291ac4b82bSMike Smith } 10301ac4b82bSMike Smith 10311ac4b82bSMike Smith /* 10321ac4b82bSMike Smith * Run normal periodic activities? 10331ac4b82bSMike Smith */ 10345792b7feSMike Smith } else if (time_second > (sc->mlx_lastpoll + 10)) { 10351ac4b82bSMike Smith sc->mlx_lastpoll = time_second; 10361ac4b82bSMike Smith 10371ac4b82bSMike Smith /* 10381ac4b82bSMike Smith * Check controller status. 10395792b7feSMike Smith * 10405792b7feSMike Smith * XXX Note that this may not actually launch a command in situations of high load. 10411ac4b82bSMike Smith */ 1042da8bb3a3SMike Smith mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY, 1043da8bb3a3SMike Smith imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry); 10441ac4b82bSMike Smith 10451ac4b82bSMike Smith /* 10461ac4b82bSMike Smith * Check system drive status. 10471ac4b82bSMike Smith * 10481ac4b82bSMike Smith * XXX This might be better left to event-driven detection, eg. I/O to an offline 10491ac4b82bSMike Smith * drive will detect it's offline, rebuilds etc. should detect the drive is back 10501ac4b82bSMike Smith * online. 10511ac4b82bSMike Smith */ 10521ac4b82bSMike Smith mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES, 10531ac4b82bSMike Smith mlx_periodic_enquiry); 10541ac4b82bSMike Smith 10551ac4b82bSMike Smith } 10561ac4b82bSMike Smith 1057421f2f7dSMike Smith /* get drive rebuild/check status */ 1058421f2f7dSMike Smith /* XXX should check sc->mlx_background if this is only valid while in progress */ 1059421f2f7dSMike Smith mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild); 1060421f2f7dSMike Smith 10615792b7feSMike Smith /* deal with possibly-missed interrupts and timed-out commands */ 10625792b7feSMike Smith mlx_done(sc); 10631ac4b82bSMike Smith 10641ac4b82bSMike Smith /* reschedule another poll next second or so */ 10651ac4b82bSMike Smith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 10661ac4b82bSMike Smith } 10671ac4b82bSMike Smith 10681ac4b82bSMike Smith /******************************************************************************** 10691ac4b82bSMike Smith * Handle the result of an ENQUIRY command instigated by periodic status polling. 10701ac4b82bSMike Smith */ 10711ac4b82bSMike Smith static void 10721ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc) 10731ac4b82bSMike Smith { 10741ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 10751ac4b82bSMike Smith 1076da8bb3a3SMike Smith debug_called(1); 10771ac4b82bSMike Smith 10781ac4b82bSMike Smith /* Command completed OK? */ 10791ac4b82bSMike Smith if (mc->mc_status != 0) { 1080da8bb3a3SMike Smith device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc)); 10811ac4b82bSMike Smith goto out; 10821ac4b82bSMike Smith } 10831ac4b82bSMike Smith 10841ac4b82bSMike Smith /* respond to command */ 10851ac4b82bSMike Smith switch(mc->mc_mailbox[0]) { 10861ac4b82bSMike Smith /* 1087da8bb3a3SMike Smith * This is currently a bit fruitless, as we don't know how to extract the eventlog 1088da8bb3a3SMike Smith * pointer yet. 1089da8bb3a3SMike Smith */ 1090da8bb3a3SMike Smith case MLX_CMD_ENQUIRY_OLD: 1091da8bb3a3SMike Smith { 1092da8bb3a3SMike Smith struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; 1093da8bb3a3SMike Smith struct mlx_enquiry_old *meo = (struct mlx_enquiry_old *)mc->mc_data; 1094da8bb3a3SMike Smith int i; 1095da8bb3a3SMike Smith 1096da8bb3a3SMike Smith /* convert data in-place to new format */ 1097da8bb3a3SMike Smith for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) { 1098da8bb3a3SMike Smith me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan; 1099da8bb3a3SMike Smith me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ; 1100da8bb3a3SMike Smith } 1101da8bb3a3SMike Smith me->me_misc_flags = 0; 1102da8bb3a3SMike Smith me->me_rebuild_count = meo->me_rebuild_count; 1103da8bb3a3SMike Smith me->me_dead_count = meo->me_dead_count; 1104da8bb3a3SMike Smith me->me_critical_sd_count = meo->me_critical_sd_count; 1105da8bb3a3SMike Smith me->me_event_log_seq_num = 0; 1106da8bb3a3SMike Smith me->me_offline_sd_count = meo->me_offline_sd_count; 1107da8bb3a3SMike Smith me->me_max_commands = meo->me_max_commands; 1108da8bb3a3SMike Smith me->me_rebuild_flag = meo->me_rebuild_flag; 1109da8bb3a3SMike Smith me->me_fwmajor = meo->me_fwmajor; 1110da8bb3a3SMike Smith me->me_fwminor = meo->me_fwminor; 1111da8bb3a3SMike Smith me->me_status_flags = meo->me_status_flags; 1112da8bb3a3SMike Smith me->me_flash_age = meo->me_flash_age; 1113da8bb3a3SMike Smith for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) { 1114da8bb3a3SMike Smith if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) { 1115da8bb3a3SMike Smith me->me_drvsize[i] = 0; /* drive beyond supported range */ 1116da8bb3a3SMike Smith } else { 1117da8bb3a3SMike Smith me->me_drvsize[i] = meo->me_drvsize[i]; 1118da8bb3a3SMike Smith } 1119da8bb3a3SMike Smith } 1120da8bb3a3SMike Smith me->me_num_sys_drvs = meo->me_num_sys_drvs; 1121da8bb3a3SMike Smith } 1122da8bb3a3SMike Smith /* FALLTHROUGH */ 1123da8bb3a3SMike Smith 1124da8bb3a3SMike Smith /* 11251ac4b82bSMike Smith * Generic controller status update. We could do more with this than just 11261ac4b82bSMike Smith * checking the event log. 11271ac4b82bSMike Smith */ 11281ac4b82bSMike Smith case MLX_CMD_ENQUIRY: 11291ac4b82bSMike Smith { 11301ac4b82bSMike Smith struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; 11311ac4b82bSMike Smith 1132421f2f7dSMike Smith if (sc->mlx_currevent == -1) { 11339eee27f1SMike Smith /* initialise our view of the event log */ 11349eee27f1SMike Smith sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num; 11355d278f5cSMike Smith } else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) { 11361ac4b82bSMike Smith /* record where current events are up to */ 11371ac4b82bSMike Smith sc->mlx_currevent = me->me_event_log_seq_num; 1138da8bb3a3SMike Smith debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent); 11391ac4b82bSMike Smith 11405d278f5cSMike Smith /* mark the event log as busy */ 11415d278f5cSMike Smith atomic_set_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY); 11425d278f5cSMike Smith 11439eee27f1SMike Smith /* drain new eventlog entries */ 11441ac4b82bSMike Smith mlx_periodic_eventlog_poll(sc); 11451ac4b82bSMike Smith } 11461ac4b82bSMike Smith break; 11471ac4b82bSMike Smith } 11481ac4b82bSMike Smith case MLX_CMD_ENQSYSDRIVE: 11491ac4b82bSMike Smith { 11501ac4b82bSMike Smith struct mlx_enq_sys_drive *mes = (struct mlx_enq_sys_drive *)mc->mc_data; 11511ac4b82bSMike Smith struct mlx_sysdrive *dr; 11521ac4b82bSMike Smith int i; 11531ac4b82bSMike Smith 11541ac4b82bSMike Smith for (i = 0, dr = &sc->mlx_sysdrive[0]; 11551ac4b82bSMike Smith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 11561ac4b82bSMike Smith i++) { 11571ac4b82bSMike Smith 11581ac4b82bSMike Smith /* has state been changed by controller? */ 11591ac4b82bSMike Smith if (dr->ms_state != mes[i].sd_state) { 11601ac4b82bSMike Smith switch(mes[i].sd_state) { 11611ac4b82bSMike Smith case MLX_SYSD_OFFLINE: 11621ac4b82bSMike Smith device_printf(dr->ms_disk, "drive offline\n"); 11631ac4b82bSMike Smith break; 11641ac4b82bSMike Smith case MLX_SYSD_ONLINE: 11651ac4b82bSMike Smith device_printf(dr->ms_disk, "drive online\n"); 11661ac4b82bSMike Smith break; 11671ac4b82bSMike Smith case MLX_SYSD_CRITICAL: 11681ac4b82bSMike Smith device_printf(dr->ms_disk, "drive critical\n"); 11691ac4b82bSMike Smith break; 11701ac4b82bSMike Smith } 11711ac4b82bSMike Smith /* save new state */ 11721ac4b82bSMike Smith dr->ms_state = mes[i].sd_state; 11731ac4b82bSMike Smith } 11741ac4b82bSMike Smith } 11751ac4b82bSMike Smith break; 11761ac4b82bSMike Smith } 11771ac4b82bSMike Smith default: 11786e551fb6SDavid E. O'Brien device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __func__, mc->mc_mailbox[0]); 11791ac4b82bSMike Smith break; 11801ac4b82bSMike Smith } 11811ac4b82bSMike Smith 11821ac4b82bSMike Smith out: 11831ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 11841ac4b82bSMike Smith mlx_releasecmd(mc); 11851ac4b82bSMike Smith } 11861ac4b82bSMike Smith 11871b4404f9SScott Long static void 11881b4404f9SScott Long mlx_eventlog_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 11891b4404f9SScott Long { 11901b4404f9SScott Long struct mlx_command *mc; 11911b4404f9SScott Long 11921b4404f9SScott Long mc = (struct mlx_command *)arg; 11931b4404f9SScott Long mlx_setup_dmamap(mc, segs, nsegments, error); 11941b4404f9SScott Long 11951b4404f9SScott Long /* build the command to get one entry */ 11961b4404f9SScott Long mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, 11971b4404f9SScott Long mc->mc_sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0); 11981b4404f9SScott Long mc->mc_complete = mlx_periodic_eventlog_respond; 11991b4404f9SScott Long mc->mc_private = mc; 12001b4404f9SScott Long 12011b4404f9SScott Long /* start the command */ 12021b4404f9SScott Long if (mlx_start(mc) != 0) { 12031b4404f9SScott Long mlx_releasecmd(mc); 12041b4404f9SScott Long free(mc->mc_data, M_DEVBUF); 12051b4404f9SScott Long mc->mc_data = NULL; 12061b4404f9SScott Long } 12071b4404f9SScott Long 12081b4404f9SScott Long } 12091b4404f9SScott Long 12101ac4b82bSMike Smith /******************************************************************************** 12111ac4b82bSMike Smith * Instigate a poll for one event log message on (sc). 12121ac4b82bSMike Smith * We only poll for one message at a time, to keep our command usage down. 12131ac4b82bSMike Smith */ 12141ac4b82bSMike Smith static void 12151ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc) 12161ac4b82bSMike Smith { 12171ac4b82bSMike Smith struct mlx_command *mc; 12181ac4b82bSMike Smith void *result = NULL; 12191b4404f9SScott Long int error = 0; 12201ac4b82bSMike Smith 1221da8bb3a3SMike Smith debug_called(1); 12221ac4b82bSMike Smith 12231ac4b82bSMike Smith /* get ourselves a command buffer */ 12241ac4b82bSMike Smith error = 1; 12251ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 12261ac4b82bSMike Smith goto out; 12271b4404f9SScott Long 12281ac4b82bSMike Smith /* allocate the response structure */ 12291b4404f9SScott Long if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, 12301b4404f9SScott Long M_NOWAIT)) == NULL) 12311ac4b82bSMike Smith goto out; 12321b4404f9SScott Long 12331ac4b82bSMike Smith /* get a command slot */ 12341ac4b82bSMike Smith if (mlx_getslot(mc)) 12351ac4b82bSMike Smith goto out; 12361ac4b82bSMike Smith 12371ac4b82bSMike Smith /* map the command so the controller can see it */ 12381ac4b82bSMike Smith mc->mc_data = result; 123933c8cb18SMike Smith mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024; 12401b4404f9SScott Long error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, 12411b4404f9SScott Long mc->mc_length, mlx_eventlog_cb, mc, BUS_DMA_NOWAIT); 12421ac4b82bSMike Smith 12431ac4b82bSMike Smith out: 124433c8cb18SMike Smith if (error != 0) { 12451ac4b82bSMike Smith if (mc != NULL) 12461ac4b82bSMike Smith mlx_releasecmd(mc); 12471b4404f9SScott Long if ((result != NULL) && (mc->mc_data != NULL)) 12481ac4b82bSMike Smith free(result, M_DEVBUF); 12491ac4b82bSMike Smith } 125033c8cb18SMike Smith } 12511ac4b82bSMike Smith 12521ac4b82bSMike Smith /******************************************************************************** 12531ac4b82bSMike Smith * Handle the result of polling for a log message, generate diagnostic output. 12541ac4b82bSMike Smith * If this wasn't the last message waiting for us, we'll go collect another. 12551ac4b82bSMike Smith */ 12561ac4b82bSMike Smith static char *mlx_sense_messages[] = { 12571ac4b82bSMike Smith "because write recovery failed", 12581ac4b82bSMike Smith "because of SCSI bus reset failure", 12591ac4b82bSMike Smith "because of double check condition", 12601ac4b82bSMike Smith "because it was removed", 12611ac4b82bSMike Smith "because of gross error on SCSI chip", 12621ac4b82bSMike Smith "because of bad tag returned from drive", 12631ac4b82bSMike Smith "because of timeout on SCSI command", 12641ac4b82bSMike Smith "because of reset SCSI command issued from system", 12651ac4b82bSMike Smith "because busy or parity error count exceeded limit", 12661ac4b82bSMike Smith "because of 'kill drive' command from system", 12671ac4b82bSMike Smith "because of selection timeout", 12681ac4b82bSMike Smith "due to SCSI phase sequence error", 12691ac4b82bSMike Smith "due to unknown status" 12701ac4b82bSMike Smith }; 12711ac4b82bSMike Smith 12721ac4b82bSMike Smith static void 12731ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc) 12741ac4b82bSMike Smith { 12751ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 12761ac4b82bSMike Smith struct mlx_eventlog_entry *el = (struct mlx_eventlog_entry *)mc->mc_data; 12771ac4b82bSMike Smith char *reason; 12781ac4b82bSMike Smith 1279da8bb3a3SMike Smith debug_called(1); 12801ac4b82bSMike Smith 12815792b7feSMike Smith sc->mlx_lastevent++; /* next message... */ 12821ac4b82bSMike Smith if (mc->mc_status == 0) { 12831ac4b82bSMike Smith 12841ac4b82bSMike Smith /* handle event log message */ 12851ac4b82bSMike Smith switch(el->el_type) { 12861ac4b82bSMike Smith /* 12871ac4b82bSMike Smith * This is the only sort of message we understand at the moment. 12881ac4b82bSMike Smith * The tests here are probably incomplete. 12891ac4b82bSMike Smith */ 12901ac4b82bSMike Smith case MLX_LOGMSG_SENSE: /* sense data */ 12911ac4b82bSMike Smith /* Mylex vendor-specific message indicating a drive was killed? */ 12921ac4b82bSMike Smith if ((el->el_sensekey == 9) && 12931ac4b82bSMike Smith (el->el_asc == 0x80)) { 12941ac4b82bSMike Smith if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) { 12951ac4b82bSMike Smith reason = mlx_sense_messages[el->el_asq]; 12961ac4b82bSMike Smith } else { 12971ac4b82bSMike Smith reason = "for unknown reason"; 12981ac4b82bSMike Smith } 12991ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n", 13001ac4b82bSMike Smith el->el_channel, el->el_target, reason); 13011ac4b82bSMike Smith } 13021ac4b82bSMike Smith /* SCSI drive was reset? */ 13031ac4b82bSMike Smith if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) { 13041ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d reset\n", 13051ac4b82bSMike Smith el->el_channel, el->el_target); 13061ac4b82bSMike Smith } 13071ac4b82bSMike Smith /* SCSI drive error? */ 13081ac4b82bSMike Smith if (!((el->el_sensekey == 0) || 13091ac4b82bSMike Smith ((el->el_sensekey == 2) && 13101ac4b82bSMike Smith (el->el_asc == 0x04) && 13111ac4b82bSMike Smith ((el->el_asq == 0x01) || 13121ac4b82bSMike Smith (el->el_asq == 0x02))))) { 13131ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n", 13141ac4b82bSMike Smith el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq); 13151ac4b82bSMike Smith device_printf(sc->mlx_dev, " info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":"); 13161ac4b82bSMike Smith } 13171ac4b82bSMike Smith break; 13181ac4b82bSMike Smith 13191ac4b82bSMike Smith default: 13201ac4b82bSMike Smith device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type); 13211ac4b82bSMike Smith break; 13221ac4b82bSMike Smith } 13231ac4b82bSMike Smith } else { 13241ac4b82bSMike Smith device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc)); 13255d278f5cSMike Smith /* give up on all the outstanding messages, as we may have come unsynched */ 13265d278f5cSMike Smith sc->mlx_lastevent = sc->mlx_currevent; 13271ac4b82bSMike Smith } 13281ac4b82bSMike Smith 13291ac4b82bSMike Smith /* dispose of command and data */ 13301ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 13311ac4b82bSMike Smith mlx_releasecmd(mc); 13321ac4b82bSMike Smith 13331ac4b82bSMike Smith /* is there another message to obtain? */ 13345d278f5cSMike Smith if (sc->mlx_lastevent != sc->mlx_currevent) { 13351ac4b82bSMike Smith mlx_periodic_eventlog_poll(sc); 13365d278f5cSMike Smith } else { 13375d278f5cSMike Smith /* clear log-busy status */ 13385d278f5cSMike Smith atomic_clear_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY); 13395d278f5cSMike Smith } 13401ac4b82bSMike Smith } 13411ac4b82bSMike Smith 13421ac4b82bSMike Smith /******************************************************************************** 1343421f2f7dSMike Smith * Handle check/rebuild operations in progress. 13441ac4b82bSMike Smith */ 13451ac4b82bSMike Smith static void 13461ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc) 13471ac4b82bSMike Smith { 13481ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 1349421f2f7dSMike Smith struct mlx_rebuild_status *mr = (struct mlx_rebuild_status *)mc->mc_data; 13501ac4b82bSMike Smith 13511ac4b82bSMike Smith switch(mc->mc_status) { 1352421f2f7dSMike Smith case 0: /* operation running, update stats */ 1353421f2f7dSMike Smith sc->mlx_rebuildstat = *mr; 1354421f2f7dSMike Smith 1355421f2f7dSMike Smith /* spontaneous rebuild/check? */ 1356421f2f7dSMike Smith if (sc->mlx_background == 0) { 1357421f2f7dSMike Smith sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS; 1358421f2f7dSMike Smith device_printf(sc->mlx_dev, "background check/rebuild operation started\n"); 1359421f2f7dSMike Smith } 13601ac4b82bSMike Smith break; 13611ac4b82bSMike Smith 1362421f2f7dSMike Smith case 0x0105: /* nothing running, finalise stats and report */ 1363421f2f7dSMike Smith switch(sc->mlx_background) { 1364421f2f7dSMike Smith case MLX_BACKGROUND_CHECK: 1365421f2f7dSMike Smith device_printf(sc->mlx_dev, "consistency check completed\n"); /* XXX print drive? */ 1366421f2f7dSMike Smith break; 1367421f2f7dSMike Smith case MLX_BACKGROUND_REBUILD: 1368421f2f7dSMike Smith device_printf(sc->mlx_dev, "drive rebuild completed\n"); /* XXX print channel/target? */ 1369421f2f7dSMike Smith break; 1370421f2f7dSMike Smith case MLX_BACKGROUND_SPONTANEOUS: 1371421f2f7dSMike Smith default: 1372421f2f7dSMike Smith /* if we have previously been non-idle, report the transition */ 1373421f2f7dSMike Smith if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) { 1374421f2f7dSMike Smith device_printf(sc->mlx_dev, "background check/rebuild operation completed\n"); 13751ac4b82bSMike Smith } 1376421f2f7dSMike Smith } 1377421f2f7dSMike Smith sc->mlx_background = 0; 1378421f2f7dSMike Smith sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; 13791ac4b82bSMike Smith break; 13801ac4b82bSMike Smith } 13811ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 13821ac4b82bSMike Smith mlx_releasecmd(mc); 13831ac4b82bSMike Smith } 13841ac4b82bSMike Smith 13851ac4b82bSMike Smith /******************************************************************************** 13861ac4b82bSMike Smith ******************************************************************************** 13871ac4b82bSMike Smith Channel Pause 13881ac4b82bSMike Smith ******************************************************************************** 13891ac4b82bSMike Smith ********************************************************************************/ 13901ac4b82bSMike Smith 13911ac4b82bSMike Smith /******************************************************************************** 13921ac4b82bSMike Smith * It's time to perform a channel pause action for (sc), either start or stop 13931ac4b82bSMike Smith * the pause. 13941ac4b82bSMike Smith */ 13951ac4b82bSMike Smith static void 13961ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc) 13971ac4b82bSMike Smith { 13981ac4b82bSMike Smith struct mlx_command *mc; 13991ac4b82bSMike Smith int failsafe, i, command; 14001ac4b82bSMike Smith 14011ac4b82bSMike Smith /* What are we doing here? */ 14021ac4b82bSMike Smith if (sc->mlx_pause.mp_when == 0) { 14031ac4b82bSMike Smith command = MLX_CMD_STARTCHANNEL; 14041ac4b82bSMike Smith failsafe = 0; 14051ac4b82bSMike Smith 14061ac4b82bSMike Smith } else { 14071ac4b82bSMike Smith command = MLX_CMD_STOPCHANNEL; 14081ac4b82bSMike Smith 14091ac4b82bSMike Smith /* 14101ac4b82bSMike Smith * Channels will always start again after the failsafe period, 14111ac4b82bSMike Smith * which is specified in multiples of 30 seconds. 14121ac4b82bSMike Smith * This constrains us to a maximum pause of 450 seconds. 14131ac4b82bSMike Smith */ 14141ac4b82bSMike Smith failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30; 14151ac4b82bSMike Smith if (failsafe > 0xf) { 14161ac4b82bSMike Smith failsafe = 0xf; 14171ac4b82bSMike Smith sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5; 14181ac4b82bSMike Smith } 14191ac4b82bSMike Smith } 14201ac4b82bSMike Smith 14211ac4b82bSMike Smith /* build commands for every channel requested */ 14229eee27f1SMike Smith for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) { 14231ac4b82bSMike Smith if ((1 << i) & sc->mlx_pause.mp_which) { 14241ac4b82bSMike Smith 14251ac4b82bSMike Smith /* get ourselves a command buffer */ 14261ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 14271ac4b82bSMike Smith goto fail; 14281ac4b82bSMike Smith /* get a command slot */ 14291ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_PRIORITY; 14301ac4b82bSMike Smith if (mlx_getslot(mc)) 14311ac4b82bSMike Smith goto fail; 14321ac4b82bSMike Smith 14331ac4b82bSMike Smith /* build the command */ 14341ac4b82bSMike Smith mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0); 14351ac4b82bSMike Smith mc->mc_complete = mlx_pause_done; 14361ac4b82bSMike Smith mc->mc_private = sc; /* XXX not needed */ 14371ac4b82bSMike Smith if (mlx_start(mc)) 14381ac4b82bSMike Smith goto fail; 14391ac4b82bSMike Smith /* command submitted OK */ 14401ac4b82bSMike Smith return; 14411ac4b82bSMike Smith 14421ac4b82bSMike Smith fail: 14431ac4b82bSMike Smith device_printf(sc->mlx_dev, "%s failed for channel %d\n", 14441ac4b82bSMike Smith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i); 14451ac4b82bSMike Smith if (mc != NULL) 14461ac4b82bSMike Smith mlx_releasecmd(mc); 14471ac4b82bSMike Smith } 14481ac4b82bSMike Smith } 14491ac4b82bSMike Smith } 14501ac4b82bSMike Smith 14511ac4b82bSMike Smith static void 14521ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc) 14531ac4b82bSMike Smith { 14541ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 14551ac4b82bSMike Smith int command = mc->mc_mailbox[0]; 14561ac4b82bSMike Smith int channel = mc->mc_mailbox[2] & 0xf; 14571ac4b82bSMike Smith 14581ac4b82bSMike Smith if (mc->mc_status != 0) { 14591ac4b82bSMike Smith device_printf(sc->mlx_dev, "%s command failed - %s\n", 14601ac4b82bSMike Smith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc)); 14611ac4b82bSMike Smith } else if (command == MLX_CMD_STOPCHANNEL) { 14621ac4b82bSMike Smith device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n", 146372c10febSPeter Wemm channel, (long)(sc->mlx_pause.mp_howlong - time_second)); 14641ac4b82bSMike Smith } else { 14651ac4b82bSMike Smith device_printf(sc->mlx_dev, "channel %d resuming\n", channel); 14661ac4b82bSMike Smith } 14671ac4b82bSMike Smith mlx_releasecmd(mc); 14681ac4b82bSMike Smith } 14691ac4b82bSMike Smith 14701ac4b82bSMike Smith /******************************************************************************** 14711ac4b82bSMike Smith ******************************************************************************** 14721ac4b82bSMike Smith Command Submission 14731ac4b82bSMike Smith ******************************************************************************** 14741ac4b82bSMike Smith ********************************************************************************/ 14751ac4b82bSMike Smith 14761b4404f9SScott Long static void 14771b4404f9SScott Long mlx_enquire_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 14781b4404f9SScott Long { 14791b4404f9SScott Long struct mlx_softc *sc; 14801b4404f9SScott Long struct mlx_command *mc; 14811b4404f9SScott Long 14821b4404f9SScott Long mc = (struct mlx_command *)arg; 14831b4404f9SScott Long if (error) 14841b4404f9SScott Long return; 14851b4404f9SScott Long 14861b4404f9SScott Long mlx_setup_dmamap(mc, segs, nsegments, error); 14871b4404f9SScott Long 14881b4404f9SScott Long /* build an enquiry command */ 14891b4404f9SScott Long sc = mc->mc_sc; 14901b4404f9SScott Long mlx_make_type2(mc, mc->mc_command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0); 14911b4404f9SScott Long 14921b4404f9SScott Long /* do we want a completion callback? */ 14931b4404f9SScott Long if (mc->mc_complete != NULL) { 14941b4404f9SScott Long if ((error = mlx_start(mc)) != 0) 14951b4404f9SScott Long return; 14961b4404f9SScott Long } else { 14971b4404f9SScott Long /* run the command in either polled or wait mode */ 14981b4404f9SScott Long if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : 14991b4404f9SScott Long mlx_poll_command(mc)) 15001b4404f9SScott Long return; 15011b4404f9SScott Long 15021b4404f9SScott Long /* command completed OK? */ 15031b4404f9SScott Long if (mc->mc_status != 0) { 15041b4404f9SScott Long device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", 15051b4404f9SScott Long mlx_diagnose_command(mc)); 15061b4404f9SScott Long return; 15071b4404f9SScott Long } 15081b4404f9SScott Long } 15091b4404f9SScott Long } 15101b4404f9SScott Long 15111ac4b82bSMike Smith /******************************************************************************** 15121ac4b82bSMike Smith * Perform an Enquiry command using a type-3 command buffer and a return a single 15131ac4b82bSMike Smith * linear result buffer. If the completion function is specified, it will 15141ac4b82bSMike Smith * be called with the completed command (and the result response will not be 15151ac4b82bSMike Smith * valid until that point). Otherwise, the command will either be busy-waited 15161ac4b82bSMike Smith * for (interrupts not enabled), or slept for. 15171ac4b82bSMike Smith */ 15181ac4b82bSMike Smith static void * 15191ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc)) 15201ac4b82bSMike Smith { 15211ac4b82bSMike Smith struct mlx_command *mc; 15221ac4b82bSMike Smith void *result; 15231ac4b82bSMike Smith int error; 15241ac4b82bSMike Smith 1525da8bb3a3SMike Smith debug_called(1); 15261ac4b82bSMike Smith 15271ac4b82bSMike Smith /* get ourselves a command buffer */ 15281ac4b82bSMike Smith error = 1; 15291ac4b82bSMike Smith result = NULL; 15301ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 15311ac4b82bSMike Smith goto out; 15321ac4b82bSMike Smith /* allocate the response structure */ 15331ac4b82bSMike Smith if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL) 15341ac4b82bSMike Smith goto out; 15351ac4b82bSMike Smith /* get a command slot */ 15361ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT; 15371ac4b82bSMike Smith if (mlx_getslot(mc)) 15381ac4b82bSMike Smith goto out; 15391ac4b82bSMike Smith 15401ac4b82bSMike Smith /* map the command so the controller can see it */ 15411ac4b82bSMike Smith mc->mc_data = result; 15421ac4b82bSMike Smith mc->mc_length = bufsize; 15431b4404f9SScott Long mc->mc_command = command; 15441ac4b82bSMike Smith 15451ac4b82bSMike Smith if (complete != NULL) { 15461ac4b82bSMike Smith mc->mc_complete = complete; 15471ac4b82bSMike Smith mc->mc_private = mc; 15481b4404f9SScott Long } 15491ac4b82bSMike Smith 15501b4404f9SScott Long error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, 15511b4404f9SScott Long mc->mc_length, mlx_enquire_cb, mc, BUS_DMA_NOWAIT); 15521b4404f9SScott Long 15531ac4b82bSMike Smith out: 15541ac4b82bSMike Smith /* we got a command, but nobody else will free it */ 15551b4404f9SScott Long if ((mc->mc_complete == NULL) && (mc != NULL)) 15561ac4b82bSMike Smith mlx_releasecmd(mc); 155733c8cb18SMike Smith /* we got an error, and we allocated a result */ 1558d4881905SScott Long if ((error != 0) && (result != NULL)) { 1559d4881905SScott Long free(result, M_DEVBUF); 1560a7700303SScott Long result = NULL; 15611ac4b82bSMike Smith } 15621ac4b82bSMike Smith return(result); 15631ac4b82bSMike Smith } 15641ac4b82bSMike Smith 15651ac4b82bSMike Smith 15661ac4b82bSMike Smith /******************************************************************************** 15671ac4b82bSMike Smith * Perform a Flush command on the nominated controller. 15681ac4b82bSMike Smith * 15691ac4b82bSMike Smith * May be called with interrupts enabled or disabled; will not return until 15701ac4b82bSMike Smith * the flush operation completes or fails. 15711ac4b82bSMike Smith */ 15721ac4b82bSMike Smith static int 15731ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc) 15741ac4b82bSMike Smith { 15751ac4b82bSMike Smith struct mlx_command *mc; 15761ac4b82bSMike Smith int error; 15771ac4b82bSMike Smith 1578da8bb3a3SMike Smith debug_called(1); 15791ac4b82bSMike Smith 15801ac4b82bSMike Smith /* get ourselves a command buffer */ 15811ac4b82bSMike Smith error = 1; 15821ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 15831ac4b82bSMike Smith goto out; 15841ac4b82bSMike Smith /* get a command slot */ 15851ac4b82bSMike Smith if (mlx_getslot(mc)) 15861ac4b82bSMike Smith goto out; 15871ac4b82bSMike Smith 15881ac4b82bSMike Smith /* build a flush command */ 15891ac4b82bSMike Smith mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0); 15901ac4b82bSMike Smith 15915792b7feSMike Smith /* can't assume that interrupts are going to work here, so play it safe */ 15925792b7feSMike Smith if (mlx_poll_command(mc)) 15931ac4b82bSMike Smith goto out; 15941ac4b82bSMike Smith 15951ac4b82bSMike Smith /* command completed OK? */ 15961ac4b82bSMike Smith if (mc->mc_status != 0) { 15971ac4b82bSMike Smith device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc)); 15981ac4b82bSMike Smith goto out; 15991ac4b82bSMike Smith } 16001ac4b82bSMike Smith 16011ac4b82bSMike Smith error = 0; /* success */ 16021ac4b82bSMike Smith out: 16031ac4b82bSMike Smith if (mc != NULL) 16041ac4b82bSMike Smith mlx_releasecmd(mc); 16051ac4b82bSMike Smith return(error); 16061ac4b82bSMike Smith } 16071ac4b82bSMike Smith 16081ac4b82bSMike Smith /******************************************************************************** 1609421f2f7dSMike Smith * Start a background consistency check on (drive). 1610421f2f7dSMike Smith * 1611421f2f7dSMike Smith * May be called with interrupts enabled or disabled; will return as soon as the 1612421f2f7dSMike Smith * operation has started or been refused. 1613421f2f7dSMike Smith */ 1614421f2f7dSMike Smith static int 1615421f2f7dSMike Smith mlx_check(struct mlx_softc *sc, int drive) 1616421f2f7dSMike Smith { 1617421f2f7dSMike Smith struct mlx_command *mc; 1618421f2f7dSMike Smith int error; 1619421f2f7dSMike Smith 1620421f2f7dSMike Smith debug_called(1); 1621421f2f7dSMike Smith 1622421f2f7dSMike Smith /* get ourselves a command buffer */ 1623421f2f7dSMike Smith error = 0x10000; 1624421f2f7dSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 1625421f2f7dSMike Smith goto out; 1626421f2f7dSMike Smith /* get a command slot */ 1627421f2f7dSMike Smith if (mlx_getslot(mc)) 1628421f2f7dSMike Smith goto out; 1629421f2f7dSMike Smith 1630421f2f7dSMike Smith /* build a checkasync command, set the "fix it" flag */ 1631421f2f7dSMike Smith mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0); 1632421f2f7dSMike Smith 1633421f2f7dSMike Smith /* start the command and wait for it to be returned */ 1634421f2f7dSMike Smith if (mlx_wait_command(mc)) 1635421f2f7dSMike Smith goto out; 1636421f2f7dSMike Smith 1637421f2f7dSMike Smith /* command completed OK? */ 1638421f2f7dSMike Smith if (mc->mc_status != 0) { 1639421f2f7dSMike Smith device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc)); 1640421f2f7dSMike Smith } else { 1641421f2f7dSMike Smith device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started"); 1642421f2f7dSMike Smith } 1643421f2f7dSMike Smith error = mc->mc_status; 1644421f2f7dSMike Smith 1645421f2f7dSMike Smith out: 1646421f2f7dSMike Smith if (mc != NULL) 1647421f2f7dSMike Smith mlx_releasecmd(mc); 1648421f2f7dSMike Smith return(error); 1649421f2f7dSMike Smith } 1650421f2f7dSMike Smith 1651421f2f7dSMike Smith /******************************************************************************** 1652421f2f7dSMike Smith * Start a background rebuild of the physical drive at (channel),(target). 16531ac4b82bSMike Smith * 16541ac4b82bSMike Smith * May be called with interrupts enabled or disabled; will return as soon as the 16551ac4b82bSMike Smith * operation has started or been refused. 16561ac4b82bSMike Smith */ 16571ac4b82bSMike Smith static int 16581ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target) 16591ac4b82bSMike Smith { 16601ac4b82bSMike Smith struct mlx_command *mc; 16611ac4b82bSMike Smith int error; 16621ac4b82bSMike Smith 1663da8bb3a3SMike Smith debug_called(1); 16641ac4b82bSMike Smith 16651ac4b82bSMike Smith /* get ourselves a command buffer */ 16661ac4b82bSMike Smith error = 0x10000; 16671ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 16681ac4b82bSMike Smith goto out; 16691ac4b82bSMike Smith /* get a command slot */ 16701ac4b82bSMike Smith if (mlx_getslot(mc)) 16711ac4b82bSMike Smith goto out; 16721ac4b82bSMike Smith 1673421f2f7dSMike Smith /* build a checkasync command, set the "fix it" flag */ 16741ac4b82bSMike Smith mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0); 16751ac4b82bSMike Smith 1676421f2f7dSMike Smith /* start the command and wait for it to be returned */ 1677421f2f7dSMike Smith if (mlx_wait_command(mc)) 16781ac4b82bSMike Smith goto out; 16791ac4b82bSMike Smith 16801ac4b82bSMike Smith /* command completed OK? */ 16811ac4b82bSMike Smith if (mc->mc_status != 0) { 16821ac4b82bSMike Smith device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc)); 16831ac4b82bSMike Smith } else { 1684421f2f7dSMike Smith device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target); 16851ac4b82bSMike Smith } 16861ac4b82bSMike Smith error = mc->mc_status; 16871ac4b82bSMike Smith 16881ac4b82bSMike Smith out: 16891ac4b82bSMike Smith if (mc != NULL) 16901ac4b82bSMike Smith mlx_releasecmd(mc); 16911ac4b82bSMike Smith return(error); 16921ac4b82bSMike Smith } 16931ac4b82bSMike Smith 16941ac4b82bSMike Smith /******************************************************************************** 16951ac4b82bSMike Smith * Run the command (mc) and return when it completes. 16961ac4b82bSMike Smith * 16971ac4b82bSMike Smith * Interrupts need to be enabled; returns nonzero on error. 16981ac4b82bSMike Smith */ 16991ac4b82bSMike Smith static int 17001ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc) 17011ac4b82bSMike Smith { 17021ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 17031ac4b82bSMike Smith int error, count; 17041ac4b82bSMike Smith 1705da8bb3a3SMike Smith debug_called(1); 17061ac4b82bSMike Smith 17071ac4b82bSMike Smith mc->mc_complete = NULL; 17081ac4b82bSMike Smith mc->mc_private = mc; /* wake us when you're done */ 17091ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 17101ac4b82bSMike Smith return(error); 17111ac4b82bSMike Smith 17121ac4b82bSMike Smith count = 0; 17131ac4b82bSMike Smith /* XXX better timeout? */ 17141ac4b82bSMike Smith while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) { 17151ac4b82bSMike Smith tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz); 17161ac4b82bSMike Smith } 17171ac4b82bSMike Smith 17181ac4b82bSMike Smith if (mc->mc_status != 0) { 1719da8bb3a3SMike Smith device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc)); 17201ac4b82bSMike Smith return(EIO); 17211ac4b82bSMike Smith } 17221ac4b82bSMike Smith return(0); 17231ac4b82bSMike Smith } 17241ac4b82bSMike Smith 17251ac4b82bSMike Smith 17261ac4b82bSMike Smith /******************************************************************************** 17271ac4b82bSMike Smith * Start the command (mc) and busy-wait for it to complete. 17281ac4b82bSMike Smith * 1729da8bb3a3SMike Smith * Should only be used when interrupts can't be relied upon. Returns 0 on 17301ac4b82bSMike Smith * success, nonzero on error. 17311ac4b82bSMike Smith * Successfully completed commands are dequeued. 17321ac4b82bSMike Smith */ 17331ac4b82bSMike Smith static int 17341ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc) 17351ac4b82bSMike Smith { 17361ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 17371ac4b82bSMike Smith int error, count, s; 17381ac4b82bSMike Smith 1739da8bb3a3SMike Smith debug_called(1); 17401ac4b82bSMike Smith 17411ac4b82bSMike Smith mc->mc_complete = NULL; 17421ac4b82bSMike Smith mc->mc_private = NULL; /* we will poll for it */ 17431ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 17441ac4b82bSMike Smith return(error); 17451ac4b82bSMike Smith 17461ac4b82bSMike Smith count = 0; 17471ac4b82bSMike Smith do { 17481ac4b82bSMike Smith /* poll for completion */ 17491ac4b82bSMike Smith mlx_done(mc->mc_sc); 1750da8bb3a3SMike Smith 1751da8bb3a3SMike Smith } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000)); 17521ac4b82bSMike Smith if (mc->mc_status != MLX_STATUS_BUSY) { 17531ac4b82bSMike Smith s = splbio(); 17544b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 17551ac4b82bSMike Smith splx(s); 17561ac4b82bSMike Smith return(0); 17571ac4b82bSMike Smith } 1758421f2f7dSMike Smith device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc)); 17591ac4b82bSMike Smith return(EIO); 17601ac4b82bSMike Smith } 17611ac4b82bSMike Smith 17621b4404f9SScott Long void 17631b4404f9SScott Long mlx_startio_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 17641b4404f9SScott Long { 17651b4404f9SScott Long struct mlx_command *mc; 17661b4404f9SScott Long struct mlxd_softc *mlxd; 17671b4404f9SScott Long struct mlx_softc *sc; 17681b4404f9SScott Long mlx_bio *bp; 17691b4404f9SScott Long int blkcount; 17701b4404f9SScott Long int driveno; 17711b4404f9SScott Long int cmd; 17721b4404f9SScott Long 17731b4404f9SScott Long mc = (struct mlx_command *)arg; 17741b4404f9SScott Long mlx_setup_dmamap(mc, segs, nsegments, error); 17751b4404f9SScott Long 17761b4404f9SScott Long sc = mc->mc_sc; 17771b4404f9SScott Long bp = mc->mc_private; 17781b4404f9SScott Long 17791b4404f9SScott Long if (MLX_BIO_IS_READ(bp)) { 17801b4404f9SScott Long mc->mc_flags |= MLX_CMD_DATAIN; 17811b4404f9SScott Long cmd = MLX_CMD_READSG; 17821b4404f9SScott Long } else { 17831b4404f9SScott Long mc->mc_flags |= MLX_CMD_DATAOUT; 17841b4404f9SScott Long cmd = MLX_CMD_WRITESG; 17851b4404f9SScott Long } 17861b4404f9SScott Long 17871b4404f9SScott Long /* build a suitable I/O command (assumes 512-byte rounded transfers) */ 17881b4404f9SScott Long mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp); 17891b4404f9SScott Long driveno = mlxd->mlxd_drive - sc->mlx_sysdrive; 17901b4404f9SScott Long blkcount = (MLX_BIO_LENGTH(bp) + MLX_BLKSIZE - 1) / MLX_BLKSIZE; 17911b4404f9SScott Long 17921b4404f9SScott Long if ((MLX_BIO_LBA(bp) + blkcount) > sc->mlx_sysdrive[driveno].ms_size) 17931b4404f9SScott Long device_printf(sc->mlx_dev, 17941b4404f9SScott Long "I/O beyond end of unit (%lld,%d > %lu)\n", 17951b4404f9SScott Long (long long)MLX_BIO_LBA(bp), blkcount, 17961b4404f9SScott Long (u_long)sc->mlx_sysdrive[driveno].ms_size); 17971b4404f9SScott Long 17981b4404f9SScott Long /* 17991b4404f9SScott Long * Build the I/O command. Note that the SG list type bits are set to zero, 18001b4404f9SScott Long * denoting the format of SG list that we are using. 18011b4404f9SScott Long */ 18021b4404f9SScott Long if (sc->mlx_iftype == MLX_IFTYPE_2) { 18031b4404f9SScott Long mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD : 18041b4404f9SScott Long MLX_CMD_READSG_OLD, 18051b4404f9SScott Long blkcount & 0xff, /* xfer length low byte */ 18061b4404f9SScott Long MLX_BIO_LBA(bp), /* physical block number */ 18071b4404f9SScott Long driveno, /* target drive number */ 18081b4404f9SScott Long mc->mc_sgphys, /* location of SG list */ 18091b4404f9SScott Long mc->mc_nsgent & 0x3f); /* size of SG list */ 18101b4404f9SScott Long } else { 18111b4404f9SScott Long mlx_make_type5(mc, cmd, 18121b4404f9SScott Long blkcount & 0xff, /* xfer length low byte */ 18131b4404f9SScott Long (driveno << 3) | ((blkcount >> 8) & 0x07), 18141b4404f9SScott Long /* target+length high 3 bits */ 18151b4404f9SScott Long MLX_BIO_LBA(bp), /* physical block number */ 18161b4404f9SScott Long mc->mc_sgphys, /* location of SG list */ 18171b4404f9SScott Long mc->mc_nsgent & 0x3f); /* size of SG list */ 18181b4404f9SScott Long } 18191b4404f9SScott Long 18201b4404f9SScott Long /* try to give command to controller */ 18211b4404f9SScott Long if (mlx_start(mc) != 0) { 18221b4404f9SScott Long /* fail the command */ 18231b4404f9SScott Long mc->mc_status = MLX_STATUS_WEDGED; 18241b4404f9SScott Long mlx_completeio(mc); 18251b4404f9SScott Long } 18261b4404f9SScott Long } 18271b4404f9SScott Long 18281ac4b82bSMike Smith /******************************************************************************** 18291ac4b82bSMike Smith * Pull as much work off the softc's work queue as possible and give it to the 18301ac4b82bSMike Smith * controller. Leave a couple of slots free for emergencies. 18311ac4b82bSMike Smith * 18321ac4b82bSMike Smith * Must be called at splbio or in an equivalent fashion that prevents 18338177437dSPoul-Henning Kamp * reentry or activity on the bioq. 18341ac4b82bSMike Smith */ 18351ac4b82bSMike Smith static void 18361ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc) 18371ac4b82bSMike Smith { 18381ac4b82bSMike Smith struct mlx_command *mc; 183915fd5d22SMike Smith mlx_bio *bp; 18404b006d7bSMike Smith int s; 18411b4404f9SScott Long int error; 18421ac4b82bSMike Smith 18435792b7feSMike Smith /* avoid reentrancy */ 18445792b7feSMike Smith if (mlx_lock_tas(sc, MLX_LOCK_STARTING)) 18455792b7feSMike Smith return; 18465792b7feSMike Smith 18471ac4b82bSMike Smith /* spin until something prevents us from doing any work */ 18484b006d7bSMike Smith s = splbio(); 18491ac4b82bSMike Smith for (;;) { 18501ac4b82bSMike Smith 18511ac4b82bSMike Smith /* see if there's work to be done */ 185215fd5d22SMike Smith if ((bp = MLX_BIO_QFIRST(sc->mlx_bioq)) == NULL) 18531ac4b82bSMike Smith break; 18541ac4b82bSMike Smith /* get a command */ 18551ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 18561ac4b82bSMike Smith break; 18571ac4b82bSMike Smith /* get a slot for the command */ 18581ac4b82bSMike Smith if (mlx_getslot(mc) != 0) { 18591ac4b82bSMike Smith mlx_releasecmd(mc); 18601ac4b82bSMike Smith break; 18611ac4b82bSMike Smith } 18621ac4b82bSMike Smith /* get the buf containing our work */ 186315fd5d22SMike Smith MLX_BIO_QREMOVE(sc->mlx_bioq, bp); 18641ac4b82bSMike Smith sc->mlx_waitbufs--; 18654b006d7bSMike Smith splx(s); 18661ac4b82bSMike Smith 18671ac4b82bSMike Smith /* connect the buf to the command */ 18681ac4b82bSMike Smith mc->mc_complete = mlx_completeio; 18691ac4b82bSMike Smith mc->mc_private = bp; 187015fd5d22SMike Smith mc->mc_data = MLX_BIO_DATA(bp); 187115fd5d22SMike Smith mc->mc_length = MLX_BIO_LENGTH(bp); 18721ac4b82bSMike Smith 18731ac4b82bSMike Smith /* map the command so the controller can work with it */ 18741b4404f9SScott Long error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, 18751b4404f9SScott Long mc->mc_length, mlx_startio_cb, mc, 0); 18761b4404f9SScott Long if (error == EINPROGRESS) { 18771b4404f9SScott Long break; 1878da8bb3a3SMike Smith } 18791ac4b82bSMike Smith 18804b006d7bSMike Smith s = splbio(); 18811ac4b82bSMike Smith } 18824b006d7bSMike Smith splx(s); 18835792b7feSMike Smith mlx_lock_clr(sc, MLX_LOCK_STARTING); 18841ac4b82bSMike Smith } 18851ac4b82bSMike Smith 18861ac4b82bSMike Smith /******************************************************************************** 18871ac4b82bSMike Smith * Handle completion of an I/O command. 18881ac4b82bSMike Smith */ 18891ac4b82bSMike Smith static void 18901ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc) 18911ac4b82bSMike Smith { 18921ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 189315fd5d22SMike Smith mlx_bio *bp = (mlx_bio *)mc->mc_private; 189415fd5d22SMike Smith struct mlxd_softc *mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp); 18951ac4b82bSMike Smith 18961ac4b82bSMike Smith if (mc->mc_status != MLX_STATUS_OK) { /* could be more verbose here? */ 189715fd5d22SMike Smith MLX_BIO_SET_ERROR(bp, EIO); 18981ac4b82bSMike Smith 18991ac4b82bSMike Smith switch(mc->mc_status) { 19001ac4b82bSMike Smith case MLX_STATUS_RDWROFFLINE: /* system drive has gone offline */ 19011ac4b82bSMike Smith device_printf(mlxd->mlxd_dev, "drive offline\n"); 1902f6b84b08SMike Smith /* should signal this with a return code */ 19031ac4b82bSMike Smith mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE; 19041ac4b82bSMike Smith break; 19051ac4b82bSMike Smith 19061ac4b82bSMike Smith default: /* other I/O error */ 19071ac4b82bSMike Smith device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc)); 19081ac4b82bSMike Smith #if 0 1909cd4ace0cSMike Smith device_printf(sc->mlx_dev, " b_bcount %ld blkcount %ld b_pblkno %d\n", 191015fd5d22SMike Smith MLX_BIO_LENGTH(bp), MLX_BIO_LENGTH(bp) / MLX_BLKSIZE, MLX_BIO_LBA(bp)); 19111ac4b82bSMike Smith device_printf(sc->mlx_dev, " %13D\n", mc->mc_mailbox, " "); 19121ac4b82bSMike Smith #endif 19131ac4b82bSMike Smith break; 19141ac4b82bSMike Smith } 19151ac4b82bSMike Smith } 19161ac4b82bSMike Smith mlx_releasecmd(mc); 19171ac4b82bSMike Smith mlxd_intr(bp); 19181ac4b82bSMike Smith } 19191ac4b82bSMike Smith 19201b4404f9SScott Long void 19211b4404f9SScott Long mlx_user_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 19221b4404f9SScott Long { 19231b4404f9SScott Long struct mlx_usercommand *mu; 19241b4404f9SScott Long struct mlx_command *mc; 19251b4404f9SScott Long struct mlx_dcdb *dcdb; 19261b4404f9SScott Long 19271b4404f9SScott Long mc = (struct mlx_command *)arg; 19281b4404f9SScott Long if (error) 19291b4404f9SScott Long return; 19301b4404f9SScott Long 19311b4404f9SScott Long mlx_setup_dmamap(mc, segs, nsegments, error); 19321b4404f9SScott Long 19331b4404f9SScott Long mu = (struct mlx_usercommand *)mc->mc_private; 19341b4404f9SScott Long dcdb = NULL; 19351b4404f9SScott Long 19361b4404f9SScott Long /* 19371b4404f9SScott Long * If this is a passthrough SCSI command, the DCDB is packed at the 19381b4404f9SScott Long * beginning of the data area. Fix up the DCDB to point to the correct 19391b4404f9SScott Long * physical address and override any bufptr supplied by the caller since 19401b4404f9SScott Long * we know what it's meant to be. 19411b4404f9SScott Long */ 19421b4404f9SScott Long if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) { 19431b4404f9SScott Long dcdb = (struct mlx_dcdb *)mc->mc_data; 19441b4404f9SScott Long dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb); 19451b4404f9SScott Long mu->mu_bufptr = 8; 19461b4404f9SScott Long } 19471b4404f9SScott Long 19481b4404f9SScott Long /* 19491b4404f9SScott Long * If there's a data buffer, fix up the command's buffer pointer. 19501b4404f9SScott Long */ 19511b4404f9SScott Long if (mu->mu_datasize > 0) { 19521b4404f9SScott Long mc->mc_mailbox[mu->mu_bufptr ] = mc->mc_dataphys & 0xff; 19531b4404f9SScott Long mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8) & 0xff; 19541b4404f9SScott Long mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff; 19551b4404f9SScott Long mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff; 19561b4404f9SScott Long } 19571b4404f9SScott Long debug(0, "command fixup"); 19581b4404f9SScott Long 19591b4404f9SScott Long /* submit the command and wait */ 19601b4404f9SScott Long if (mlx_wait_command(mc) != 0) 19611b4404f9SScott Long return; 19621b4404f9SScott Long 19631b4404f9SScott Long } 19641b4404f9SScott Long 19651ac4b82bSMike Smith /******************************************************************************** 19661ac4b82bSMike Smith * Take a command from user-space and try to run it. 1967da8bb3a3SMike Smith * 1968da8bb3a3SMike Smith * XXX Note that this can't perform very much in the way of error checking, and 1969da8bb3a3SMike Smith * as such, applications _must_ be considered trustworthy. 1970da8bb3a3SMike Smith * XXX Commands using S/G for data are not supported. 19711ac4b82bSMike Smith */ 19721ac4b82bSMike Smith static int 19731ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu) 19741ac4b82bSMike Smith { 19751ac4b82bSMike Smith struct mlx_command *mc; 19761ac4b82bSMike Smith void *kbuf; 19771ac4b82bSMike Smith int error; 19781ac4b82bSMike Smith 1979da8bb3a3SMike Smith debug_called(0); 1980da8bb3a3SMike Smith 19811ac4b82bSMike Smith kbuf = NULL; 19821ac4b82bSMike Smith mc = NULL; 19831ac4b82bSMike Smith error = ENOMEM; 1984da8bb3a3SMike Smith 1985da8bb3a3SMike Smith /* get ourselves a command and copy in from user space */ 19861ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 19871ac4b82bSMike Smith goto out; 19881ac4b82bSMike Smith bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox)); 1989da8bb3a3SMike Smith debug(0, "got command buffer"); 1990da8bb3a3SMike Smith 19911b4404f9SScott Long /* 19921b4404f9SScott Long * if we need a buffer for data transfer, allocate one and copy in its 19931b4404f9SScott Long * initial contents 19941b4404f9SScott Long */ 1995da8bb3a3SMike Smith if (mu->mu_datasize > 0) { 1996bf61e266SKris Kennaway if (mu->mu_datasize > MAXPHYS) 1997bf61e266SKris Kennaway return (EINVAL); 1998a163d034SWarner Losh if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) || 1999da8bb3a3SMike Smith (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))) 20001ac4b82bSMike Smith goto out; 2001da8bb3a3SMike Smith debug(0, "got kernel buffer"); 2002da8bb3a3SMike Smith } 20031ac4b82bSMike Smith 20041ac4b82bSMike Smith /* get a command slot */ 20051ac4b82bSMike Smith if (mlx_getslot(mc)) 20061ac4b82bSMike Smith goto out; 2007da8bb3a3SMike Smith debug(0, "got a slot"); 20081ac4b82bSMike Smith 2009da8bb3a3SMike Smith if (mu->mu_datasize > 0) { 2010da8bb3a3SMike Smith 2011da8bb3a3SMike Smith /* range check the pointer to physical buffer address */ 20121b4404f9SScott Long if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) - 20131b4404f9SScott Long sizeof(u_int32_t)))) { 2014da8bb3a3SMike Smith error = EINVAL; 2015da8bb3a3SMike Smith goto out; 2016da8bb3a3SMike Smith } 2017da8bb3a3SMike Smith } 2018da8bb3a3SMike Smith 20191b4404f9SScott Long /* map the command so the controller can see it */ 20201b4404f9SScott Long mc->mc_data = kbuf; 20211b4404f9SScott Long mc->mc_length = mu->mu_datasize; 20221b4404f9SScott Long mc->mc_private = mu; 20231b4404f9SScott Long error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, 20241b4404f9SScott Long mc->mc_length, mlx_user_cb, mc, BUS_DMA_NOWAIT); 20251ac4b82bSMike Smith 20261ac4b82bSMike Smith /* copy out status and data */ 20271ac4b82bSMike Smith mu->mu_status = mc->mc_status; 20281b4404f9SScott Long if ((mu->mu_datasize > 0) && 20291b4404f9SScott Long ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize)))) 20301ac4b82bSMike Smith goto out; 20311b4404f9SScott Long 20321ac4b82bSMike Smith error = 0; 20331ac4b82bSMike Smith 20341ac4b82bSMike Smith out: 20351ac4b82bSMike Smith mlx_releasecmd(mc); 20361ac4b82bSMike Smith if (kbuf != NULL) 20371ac4b82bSMike Smith free(kbuf, M_DEVBUF); 20381ac4b82bSMike Smith return(error); 20391ac4b82bSMike Smith } 20401ac4b82bSMike Smith 20411ac4b82bSMike Smith /******************************************************************************** 20421ac4b82bSMike Smith ******************************************************************************** 20431ac4b82bSMike Smith Command I/O to Controller 20441ac4b82bSMike Smith ******************************************************************************** 20451ac4b82bSMike Smith ********************************************************************************/ 20461ac4b82bSMike Smith 20471ac4b82bSMike Smith /******************************************************************************** 20481ac4b82bSMike Smith * Find a free command slot for (mc). 20491ac4b82bSMike Smith * 20501ac4b82bSMike Smith * Don't hand out a slot to a normal-priority command unless there are at least 20511ac4b82bSMike Smith * 4 slots free for priority commands. 20521ac4b82bSMike Smith */ 20531ac4b82bSMike Smith static int 20541ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc) 20551ac4b82bSMike Smith { 20561ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 2057baff09dbSMike Smith int s, slot, limit; 20581ac4b82bSMike Smith 2059da8bb3a3SMike Smith debug_called(1); 20601ac4b82bSMike Smith 2061baff09dbSMike Smith /* 2062baff09dbSMike Smith * Enforce slot-usage limit, if we have the required information. 2063baff09dbSMike Smith */ 2064baff09dbSMike Smith if (sc->mlx_enq2 != NULL) { 2065baff09dbSMike Smith limit = sc->mlx_enq2->me_max_commands; 2066baff09dbSMike Smith } else { 2067baff09dbSMike Smith limit = 2; 2068baff09dbSMike Smith } 2069baff09dbSMike Smith if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? limit : limit - 4)) 20701ac4b82bSMike Smith return(EBUSY); 20711ac4b82bSMike Smith 20721ac4b82bSMike Smith /* 20731ac4b82bSMike Smith * Allocate an outstanding command slot 20741ac4b82bSMike Smith * 20751ac4b82bSMike Smith * XXX linear search is slow 20761ac4b82bSMike Smith */ 20771ac4b82bSMike Smith s = splbio(); 2078baff09dbSMike Smith for (slot = 0; slot < limit; slot++) { 2079da8bb3a3SMike Smith debug(2, "try slot %d", slot); 20801ac4b82bSMike Smith if (sc->mlx_busycmd[slot] == NULL) 20811ac4b82bSMike Smith break; 20821ac4b82bSMike Smith } 2083baff09dbSMike Smith if (slot < limit) { 20841ac4b82bSMike Smith sc->mlx_busycmd[slot] = mc; 20851ac4b82bSMike Smith sc->mlx_busycmds++; 20861ac4b82bSMike Smith } 20871ac4b82bSMike Smith splx(s); 20881ac4b82bSMike Smith 20891ac4b82bSMike Smith /* out of slots? */ 2090baff09dbSMike Smith if (slot >= limit) 20911ac4b82bSMike Smith return(EBUSY); 20921ac4b82bSMike Smith 2093da8bb3a3SMike Smith debug(2, "got slot %d", slot); 20941ac4b82bSMike Smith mc->mc_slot = slot; 20951ac4b82bSMike Smith return(0); 20961ac4b82bSMike Smith } 20971ac4b82bSMike Smith 20981ac4b82bSMike Smith /******************************************************************************** 20991ac4b82bSMike Smith * Map/unmap (mc)'s data in the controller's addressable space. 21001ac4b82bSMike Smith */ 21011ac4b82bSMike Smith static void 21021b4404f9SScott Long mlx_setup_dmamap(struct mlx_command *mc, bus_dma_segment_t *segs, int nsegments, 21031b4404f9SScott Long int error) 21041ac4b82bSMike Smith { 21051ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 21061ac4b82bSMike Smith struct mlx_sgentry *sg; 21071ac4b82bSMike Smith int i; 21081ac4b82bSMike Smith 2109da8bb3a3SMike Smith debug_called(1); 21101ac4b82bSMike Smith 2111baff09dbSMike Smith /* XXX should be unnecessary */ 2112baff09dbSMike Smith if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg)) 21131b4404f9SScott Long panic("MLX: too many s/g segments (%d, max %d)", nsegments, 21141b4404f9SScott Long sc->mlx_enq2->me_max_sg); 2115baff09dbSMike Smith 21161ac4b82bSMike Smith /* get base address of s/g table */ 2117baff09dbSMike Smith sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG); 21181ac4b82bSMike Smith 21191ac4b82bSMike Smith /* save s/g table information in command */ 21201ac4b82bSMike Smith mc->mc_nsgent = nsegments; 21211b4404f9SScott Long mc->mc_sgphys = sc->mlx_sgbusaddr + 21221b4404f9SScott Long (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry)); 21231ac4b82bSMike Smith mc->mc_dataphys = segs[0].ds_addr; 21241ac4b82bSMike Smith 21251ac4b82bSMike Smith /* populate s/g table */ 21261ac4b82bSMike Smith for (i = 0; i < nsegments; i++, sg++) { 21271ac4b82bSMike Smith sg->sg_addr = segs[i].ds_addr; 21281ac4b82bSMike Smith sg->sg_count = segs[i].ds_len; 21291ac4b82bSMike Smith } 21301ac4b82bSMike Smith 21311b4404f9SScott Long /* Make sure the buffers are visible on the bus. */ 21321ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAIN) 21331b4404f9SScott Long bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, 21341b4404f9SScott Long BUS_DMASYNC_PREREAD); 21351ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAOUT) 21361b4404f9SScott Long bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, 21371b4404f9SScott Long BUS_DMASYNC_PREWRITE); 21381ac4b82bSMike Smith } 21391ac4b82bSMike Smith 21401ac4b82bSMike Smith static void 21411ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc) 21421ac4b82bSMike Smith { 21431ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 21441ac4b82bSMike Smith 2145da8bb3a3SMike Smith debug_called(1); 21461ac4b82bSMike Smith 21471ac4b82bSMike Smith /* if the command involved data at all */ 21481ac4b82bSMike Smith if (mc->mc_data != NULL) { 21491ac4b82bSMike Smith 21501ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAIN) 21511ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD); 21521ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAOUT) 21531ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE); 21541ac4b82bSMike Smith 21551ac4b82bSMike Smith bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap); 21561ac4b82bSMike Smith } 21571ac4b82bSMike Smith } 21581ac4b82bSMike Smith 21591ac4b82bSMike Smith /******************************************************************************** 21605792b7feSMike Smith * Try to deliver (mc) to the controller. 21611ac4b82bSMike Smith * 21621ac4b82bSMike Smith * Can be called at any interrupt level, with or without interrupts enabled. 21631ac4b82bSMike Smith */ 21641ac4b82bSMike Smith static int 21651ac4b82bSMike Smith mlx_start(struct mlx_command *mc) 21661ac4b82bSMike Smith { 21671ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 21685792b7feSMike Smith int i, s, done; 21691ac4b82bSMike Smith 2170da8bb3a3SMike Smith debug_called(1); 21711ac4b82bSMike Smith 21721ac4b82bSMike Smith /* save the slot number as ident so we can handle this command when complete */ 21731ac4b82bSMike Smith mc->mc_mailbox[0x1] = mc->mc_slot; 21741ac4b82bSMike Smith 21754b006d7bSMike Smith /* mark the command as currently being processed */ 21761ac4b82bSMike Smith mc->mc_status = MLX_STATUS_BUSY; 21771ac4b82bSMike Smith 21785792b7feSMike Smith /* set a default 60-second timeout XXX tunable? XXX not currently used */ 21795792b7feSMike Smith mc->mc_timeout = time_second + 60; 21801ac4b82bSMike Smith 21811ac4b82bSMike Smith /* spin waiting for the mailbox */ 21821ac4b82bSMike Smith for (i = 100000, done = 0; (i > 0) && !done; i--) { 21831ac4b82bSMike Smith s = splbio(); 21844b006d7bSMike Smith if (sc->mlx_tryqueue(sc, mc)) { 21854b006d7bSMike Smith done = 1; 21864b006d7bSMike Smith /* move command to work queue */ 21874b006d7bSMike Smith TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link); 21884b006d7bSMike Smith } 21895792b7feSMike Smith splx(s); /* drop spl to allow completion interrupts */ 21901ac4b82bSMike Smith } 21911ac4b82bSMike Smith 21921ac4b82bSMike Smith /* command is enqueued */ 21931ac4b82bSMike Smith if (done) 21941ac4b82bSMike Smith return(0); 21951ac4b82bSMike Smith 21961ac4b82bSMike Smith /* 21971ac4b82bSMike Smith * We couldn't get the controller to take the command. Revoke the slot 21981ac4b82bSMike Smith * that the command was given and return it with a bad status. 21991ac4b82bSMike Smith */ 22001ac4b82bSMike Smith sc->mlx_busycmd[mc->mc_slot] = NULL; 22011ac4b82bSMike Smith device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n"); 22021ac4b82bSMike Smith mc->mc_status = MLX_STATUS_WEDGED; 22035792b7feSMike Smith mlx_complete(sc); 22041ac4b82bSMike Smith return(EIO); 22051ac4b82bSMike Smith } 22061ac4b82bSMike Smith 22071ac4b82bSMike Smith /******************************************************************************** 22085792b7feSMike Smith * Poll the controller (sc) for completed commands. 22095792b7feSMike Smith * Update command status and free slots for reuse. If any slots were freed, 22105792b7feSMike Smith * new commands may be posted. 22111ac4b82bSMike Smith * 22125792b7feSMike Smith * Returns nonzero if one or more commands were completed. 22131ac4b82bSMike Smith */ 22141ac4b82bSMike Smith static int 22151ac4b82bSMike Smith mlx_done(struct mlx_softc *sc) 22161ac4b82bSMike Smith { 22171ac4b82bSMike Smith struct mlx_command *mc; 22185792b7feSMike Smith int s, result; 22191ac4b82bSMike Smith u_int8_t slot; 22201ac4b82bSMike Smith u_int16_t status; 22211ac4b82bSMike Smith 2222da8bb3a3SMike Smith debug_called(2); 22231ac4b82bSMike Smith 22245792b7feSMike Smith result = 0; 22251ac4b82bSMike Smith 22265792b7feSMike Smith /* loop collecting completed commands */ 22274b006d7bSMike Smith s = splbio(); 22285792b7feSMike Smith for (;;) { 22295792b7feSMike Smith /* poll for a completed command's identifier and status */ 22301ac4b82bSMike Smith if (sc->mlx_findcomplete(sc, &slot, &status)) { 22315792b7feSMike Smith result = 1; 22321ac4b82bSMike Smith mc = sc->mlx_busycmd[slot]; /* find command */ 22331ac4b82bSMike Smith if (mc != NULL) { /* paranoia */ 22341ac4b82bSMike Smith if (mc->mc_status == MLX_STATUS_BUSY) { 22351ac4b82bSMike Smith mc->mc_status = status; /* save status */ 22361ac4b82bSMike Smith 22371ac4b82bSMike Smith /* free slot for reuse */ 22381ac4b82bSMike Smith sc->mlx_busycmd[slot] = NULL; 22391ac4b82bSMike Smith sc->mlx_busycmds--; 22401ac4b82bSMike Smith } else { 22411ac4b82bSMike Smith device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot); 22421ac4b82bSMike Smith } 22431ac4b82bSMike Smith } else { 22441ac4b82bSMike Smith device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot); 22451ac4b82bSMike Smith } 22465792b7feSMike Smith } else { 22475792b7feSMike Smith break; 22481ac4b82bSMike Smith } 22495792b7feSMike Smith } 2250baff09dbSMike Smith splx(s); 22511ac4b82bSMike Smith 22525792b7feSMike Smith /* if we've completed any commands, try posting some more */ 22535792b7feSMike Smith if (result) 22545792b7feSMike Smith mlx_startio(sc); 22555792b7feSMike Smith 22565792b7feSMike Smith /* handle completion and timeouts */ 22575792b7feSMike Smith mlx_complete(sc); 22585792b7feSMike Smith 22595792b7feSMike Smith return(result); 22601ac4b82bSMike Smith } 22611ac4b82bSMike Smith 22621ac4b82bSMike Smith /******************************************************************************** 22635792b7feSMike Smith * Perform post-completion processing for commands on (sc). 22641ac4b82bSMike Smith */ 22651ac4b82bSMike Smith static void 22661ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc) 22671ac4b82bSMike Smith { 22681ac4b82bSMike Smith struct mlx_command *mc, *nc; 22691ac4b82bSMike Smith int s, count; 22701ac4b82bSMike Smith 2271da8bb3a3SMike Smith debug_called(2); 22721ac4b82bSMike Smith 22735792b7feSMike Smith /* avoid reentrancy XXX might want to signal and request a restart */ 22745792b7feSMike Smith if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING)) 22755792b7feSMike Smith return; 22765792b7feSMike Smith 22771ac4b82bSMike Smith s = splbio(); 22781ac4b82bSMike Smith count = 0; 22791ac4b82bSMike Smith 22805792b7feSMike Smith /* scan the list of busy/done commands */ 22814b006d7bSMike Smith mc = TAILQ_FIRST(&sc->mlx_work); 22821ac4b82bSMike Smith while (mc != NULL) { 22831ac4b82bSMike Smith nc = TAILQ_NEXT(mc, mc_link); 22841ac4b82bSMike Smith 22855792b7feSMike Smith /* Command has been completed in some fashion */ 22864b006d7bSMike Smith if (mc->mc_status != MLX_STATUS_BUSY) { 22874b006d7bSMike Smith 22885792b7feSMike Smith /* unmap the command's data buffer */ 22895792b7feSMike Smith mlx_unmapcmd(mc); 22901ac4b82bSMike Smith /* 22911ac4b82bSMike Smith * Does the command have a completion handler? 22921ac4b82bSMike Smith */ 22931ac4b82bSMike Smith if (mc->mc_complete != NULL) { 22941ac4b82bSMike Smith /* remove from list and give to handler */ 22954b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 22961ac4b82bSMike Smith mc->mc_complete(mc); 22971ac4b82bSMike Smith 22981ac4b82bSMike Smith /* 22991ac4b82bSMike Smith * Is there a sleeper waiting on this command? 23001ac4b82bSMike Smith */ 23011ac4b82bSMike Smith } else if (mc->mc_private != NULL) { /* sleeping caller wants to know about it */ 23021ac4b82bSMike Smith 23031ac4b82bSMike Smith /* remove from list and wake up sleeper */ 23044b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 23051ac4b82bSMike Smith wakeup_one(mc->mc_private); 23061ac4b82bSMike Smith 23071ac4b82bSMike Smith /* 23081ac4b82bSMike Smith * Leave the command for a caller that's polling for it. 23091ac4b82bSMike Smith */ 23101ac4b82bSMike Smith } else { 23111ac4b82bSMike Smith } 23124b006d7bSMike Smith } 23131ac4b82bSMike Smith mc = nc; 23141ac4b82bSMike Smith } 23151ac4b82bSMike Smith splx(s); 23161ac4b82bSMike Smith 23175792b7feSMike Smith mlx_lock_clr(sc, MLX_LOCK_COMPLETING); 23181ac4b82bSMike Smith } 23191ac4b82bSMike Smith 23201ac4b82bSMike Smith /******************************************************************************** 23211ac4b82bSMike Smith ******************************************************************************** 23221ac4b82bSMike Smith Command Buffer Management 23231ac4b82bSMike Smith ******************************************************************************** 23241ac4b82bSMike Smith ********************************************************************************/ 23251ac4b82bSMike Smith 23261ac4b82bSMike Smith /******************************************************************************** 23271ac4b82bSMike Smith * Get a new command buffer. 23281ac4b82bSMike Smith * 23291ac4b82bSMike Smith * This may return NULL in low-memory cases. 23301ac4b82bSMike Smith * 23311ac4b82bSMike Smith * Note that using malloc() is expensive (the command buffer is << 1 page) but 23321ac4b82bSMike Smith * necessary if we are to be a loadable module before the zone allocator is fixed. 23331ac4b82bSMike Smith * 23341ac4b82bSMike Smith * If possible, we recycle a command buffer that's been used before. 23351ac4b82bSMike Smith * 23361ac4b82bSMike Smith * XXX Note that command buffers are not cleaned out - it is the caller's 23371ac4b82bSMike Smith * responsibility to ensure that all required fields are filled in before 23381ac4b82bSMike Smith * using a buffer. 23391ac4b82bSMike Smith */ 23401ac4b82bSMike Smith static struct mlx_command * 23411ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc) 23421ac4b82bSMike Smith { 23431ac4b82bSMike Smith struct mlx_command *mc; 23441ac4b82bSMike Smith int error; 23451ac4b82bSMike Smith int s; 23461ac4b82bSMike Smith 2347da8bb3a3SMike Smith debug_called(1); 23481ac4b82bSMike Smith 23491ac4b82bSMike Smith s = splbio(); 23501ac4b82bSMike Smith if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) 23511ac4b82bSMike Smith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 23521ac4b82bSMike Smith splx(s); 23531ac4b82bSMike Smith 23541ac4b82bSMike Smith /* allocate a new command buffer? */ 23551ac4b82bSMike Smith if (mc == NULL) { 2356ca89ee27SDavid Malone mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT | M_ZERO); 23571ac4b82bSMike Smith if (mc != NULL) { 23581ac4b82bSMike Smith mc->mc_sc = sc; 23591ac4b82bSMike Smith error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap); 23601ac4b82bSMike Smith if (error) { 23611ac4b82bSMike Smith free(mc, M_DEVBUF); 23621ac4b82bSMike Smith return(NULL); 23631ac4b82bSMike Smith } 23641ac4b82bSMike Smith } 23651ac4b82bSMike Smith } 23661ac4b82bSMike Smith return(mc); 23671ac4b82bSMike Smith } 23681ac4b82bSMike Smith 23691ac4b82bSMike Smith /******************************************************************************** 23701ac4b82bSMike Smith * Release a command buffer for recycling. 23711ac4b82bSMike Smith * 23721ac4b82bSMike Smith * XXX It might be a good idea to limit the number of commands we save for reuse 23731ac4b82bSMike Smith * if it's shown that this list bloats out massively. 23741ac4b82bSMike Smith */ 23751ac4b82bSMike Smith static void 23761ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc) 23771ac4b82bSMike Smith { 23781ac4b82bSMike Smith int s; 23791ac4b82bSMike Smith 2380da8bb3a3SMike Smith debug_called(1); 23811ac4b82bSMike Smith 23821ac4b82bSMike Smith s = splbio(); 23831ac4b82bSMike Smith TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link); 23841ac4b82bSMike Smith splx(s); 23851ac4b82bSMike Smith } 23861ac4b82bSMike Smith 23871ac4b82bSMike Smith /******************************************************************************** 23881ac4b82bSMike Smith * Permanently discard a command buffer. 23891ac4b82bSMike Smith */ 23901ac4b82bSMike Smith static void 23911ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc) 23921ac4b82bSMike Smith { 23931ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 23941ac4b82bSMike Smith 2395da8bb3a3SMike Smith debug_called(1); 23961ac4b82bSMike Smith bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap); 23971ac4b82bSMike Smith free(mc, M_DEVBUF); 23981ac4b82bSMike Smith } 23991ac4b82bSMike Smith 24001ac4b82bSMike Smith 24011ac4b82bSMike Smith /******************************************************************************** 24021ac4b82bSMike Smith ******************************************************************************** 24031ac4b82bSMike Smith Type 3 interface accessor methods 24041ac4b82bSMike Smith ******************************************************************************** 24051ac4b82bSMike Smith ********************************************************************************/ 24061ac4b82bSMike Smith 24071ac4b82bSMike Smith /******************************************************************************** 24081ac4b82bSMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 24091ac4b82bSMike Smith * (the controller is not ready to take a command). 24101ac4b82bSMike Smith * 24111ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 24121ac4b82bSMike Smith */ 24131ac4b82bSMike Smith static int 24141ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 24151ac4b82bSMike Smith { 24161ac4b82bSMike Smith int i; 24171ac4b82bSMike Smith 2418da8bb3a3SMike Smith debug_called(2); 24191ac4b82bSMike Smith 24201ac4b82bSMike Smith /* ready for our command? */ 24211ac4b82bSMike Smith if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) { 24221ac4b82bSMike Smith /* copy mailbox data to window */ 24231ac4b82bSMike Smith for (i = 0; i < 13; i++) 24241ac4b82bSMike Smith MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 24251ac4b82bSMike Smith 24261ac4b82bSMike Smith /* post command */ 2427f6b84b08SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL); 24281ac4b82bSMike Smith return(1); 24291ac4b82bSMike Smith } 24301ac4b82bSMike Smith return(0); 24311ac4b82bSMike Smith } 24321ac4b82bSMike Smith 24331ac4b82bSMike Smith /******************************************************************************** 24341ac4b82bSMike Smith * See if a command has been completed, if so acknowledge its completion 24351ac4b82bSMike Smith * and recover the slot number and status code. 24361ac4b82bSMike Smith * 24371ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 24381ac4b82bSMike Smith */ 24391ac4b82bSMike Smith static int 24401ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 24411ac4b82bSMike Smith { 24421ac4b82bSMike Smith 2443da8bb3a3SMike Smith debug_called(2); 24441ac4b82bSMike Smith 24451ac4b82bSMike Smith /* status available? */ 24461ac4b82bSMike Smith if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) { 24471ac4b82bSMike Smith *slot = MLX_V3_GET_STATUS_IDENT(sc); /* get command identifier */ 24481ac4b82bSMike Smith *status = MLX_V3_GET_STATUS(sc); /* get status */ 24491ac4b82bSMike Smith 24501ac4b82bSMike Smith /* acknowledge completion */ 2451f6b84b08SMike Smith MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL); 2452f6b84b08SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); 24531ac4b82bSMike Smith return(1); 24541ac4b82bSMike Smith } 24551ac4b82bSMike Smith return(0); 24561ac4b82bSMike Smith } 24571ac4b82bSMike Smith 24581ac4b82bSMike Smith /******************************************************************************** 24591ac4b82bSMike Smith * Enable/disable interrupts as requested. (No acknowledge required) 24601ac4b82bSMike Smith * 24611ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 24621ac4b82bSMike Smith */ 24631ac4b82bSMike Smith static void 24641ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action) 24651ac4b82bSMike Smith { 2466da8bb3a3SMike Smith debug_called(1); 24671ac4b82bSMike Smith 24681ac4b82bSMike Smith switch(action) { 24691ac4b82bSMike Smith case MLX_INTACTION_DISABLE: 24701ac4b82bSMike Smith MLX_V3_PUT_IER(sc, 0); 24711ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 24721ac4b82bSMike Smith break; 24731ac4b82bSMike Smith case MLX_INTACTION_ENABLE: 24741ac4b82bSMike Smith MLX_V3_PUT_IER(sc, 1); 24751ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_INTEN; 24761ac4b82bSMike Smith break; 24771ac4b82bSMike Smith } 24781ac4b82bSMike Smith } 24791ac4b82bSMike Smith 2480da8bb3a3SMike Smith /******************************************************************************** 2481da8bb3a3SMike Smith * Poll for firmware error codes during controller initialisation. 2482da8bb3a3SMike Smith * Returns 0 if initialisation is complete, 1 if still in progress but no 2483da8bb3a3SMike Smith * error has been fetched, 2 if an error has been retrieved. 2484da8bb3a3SMike Smith */ 2485da8bb3a3SMike Smith static int 2486da8bb3a3SMike Smith mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 2487da8bb3a3SMike Smith { 2488da8bb3a3SMike Smith u_int8_t fwerror; 2489da8bb3a3SMike Smith static int initted = 0; 2490da8bb3a3SMike Smith 2491da8bb3a3SMike Smith debug_called(2); 2492da8bb3a3SMike Smith 2493da8bb3a3SMike Smith /* first time around, clear any hardware completion status */ 2494da8bb3a3SMike Smith if (!initted) { 2495da8bb3a3SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); 2496da8bb3a3SMike Smith DELAY(1000); 2497da8bb3a3SMike Smith initted = 1; 2498da8bb3a3SMike Smith } 2499da8bb3a3SMike Smith 2500da8bb3a3SMike Smith /* init in progress? */ 2501da8bb3a3SMike Smith if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY)) 2502da8bb3a3SMike Smith return(0); 2503da8bb3a3SMike Smith 2504da8bb3a3SMike Smith /* test error value */ 2505da8bb3a3SMike Smith fwerror = MLX_V3_GET_FWERROR(sc); 2506da8bb3a3SMike Smith if (!(fwerror & MLX_V3_FWERROR_PEND)) 2507da8bb3a3SMike Smith return(1); 2508da8bb3a3SMike Smith 2509da8bb3a3SMike Smith /* mask status pending bit, fetch status */ 2510da8bb3a3SMike Smith *error = fwerror & ~MLX_V3_FWERROR_PEND; 2511da8bb3a3SMike Smith *param1 = MLX_V3_GET_FWERROR_PARAM1(sc); 2512da8bb3a3SMike Smith *param2 = MLX_V3_GET_FWERROR_PARAM2(sc); 2513da8bb3a3SMike Smith 2514da8bb3a3SMike Smith /* acknowledge */ 2515da8bb3a3SMike Smith MLX_V3_PUT_FWERROR(sc, 0); 2516da8bb3a3SMike Smith 2517da8bb3a3SMike Smith return(2); 2518da8bb3a3SMike Smith } 25191ac4b82bSMike Smith 25201ac4b82bSMike Smith /******************************************************************************** 25211ac4b82bSMike Smith ******************************************************************************** 2522f6b84b08SMike Smith Type 4 interface accessor methods 2523f6b84b08SMike Smith ******************************************************************************** 2524f6b84b08SMike Smith ********************************************************************************/ 2525f6b84b08SMike Smith 2526f6b84b08SMike Smith /******************************************************************************** 2527f6b84b08SMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 2528f6b84b08SMike Smith * (the controller is not ready to take a command). 2529f6b84b08SMike Smith * 2530f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2531f6b84b08SMike Smith */ 2532f6b84b08SMike Smith static int 2533f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 2534f6b84b08SMike Smith { 2535f6b84b08SMike Smith int i; 2536f6b84b08SMike Smith 2537da8bb3a3SMike Smith debug_called(2); 2538f6b84b08SMike Smith 2539f6b84b08SMike Smith /* ready for our command? */ 2540f6b84b08SMike Smith if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) { 2541f6b84b08SMike Smith /* copy mailbox data to window */ 2542f6b84b08SMike Smith for (i = 0; i < 13; i++) 2543f6b84b08SMike Smith MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 2544f6b84b08SMike Smith 2545da8bb3a3SMike Smith /* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */ 2546da8bb3a3SMike Smith bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH, 2547da8bb3a3SMike Smith BUS_SPACE_BARRIER_WRITE); 2548da8bb3a3SMike Smith 2549f6b84b08SMike Smith /* post command */ 2550f6b84b08SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD); 2551f6b84b08SMike Smith return(1); 2552f6b84b08SMike Smith } 2553f6b84b08SMike Smith return(0); 2554f6b84b08SMike Smith } 2555f6b84b08SMike Smith 2556f6b84b08SMike Smith /******************************************************************************** 2557f6b84b08SMike Smith * See if a command has been completed, if so acknowledge its completion 2558f6b84b08SMike Smith * and recover the slot number and status code. 2559f6b84b08SMike Smith * 2560f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2561f6b84b08SMike Smith */ 2562f6b84b08SMike Smith static int 2563f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 2564f6b84b08SMike Smith { 2565f6b84b08SMike Smith 2566da8bb3a3SMike Smith debug_called(2); 2567f6b84b08SMike Smith 2568f6b84b08SMike Smith /* status available? */ 2569f6b84b08SMike Smith if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) { 2570f6b84b08SMike Smith *slot = MLX_V4_GET_STATUS_IDENT(sc); /* get command identifier */ 2571f6b84b08SMike Smith *status = MLX_V4_GET_STATUS(sc); /* get status */ 2572f6b84b08SMike Smith 2573f6b84b08SMike Smith /* acknowledge completion */ 2574f6b84b08SMike Smith MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK); 2575f6b84b08SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); 2576f6b84b08SMike Smith return(1); 2577f6b84b08SMike Smith } 2578f6b84b08SMike Smith return(0); 2579f6b84b08SMike Smith } 2580f6b84b08SMike Smith 2581f6b84b08SMike Smith /******************************************************************************** 2582f6b84b08SMike Smith * Enable/disable interrupts as requested. 2583f6b84b08SMike Smith * 2584f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2585f6b84b08SMike Smith */ 2586f6b84b08SMike Smith static void 2587f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action) 2588f6b84b08SMike Smith { 2589da8bb3a3SMike Smith debug_called(1); 2590f6b84b08SMike Smith 2591f6b84b08SMike Smith switch(action) { 2592f6b84b08SMike Smith case MLX_INTACTION_DISABLE: 2593f6b84b08SMike Smith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT); 2594f6b84b08SMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 2595f6b84b08SMike Smith break; 2596f6b84b08SMike Smith case MLX_INTACTION_ENABLE: 2597f6b84b08SMike Smith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT); 2598f6b84b08SMike Smith sc->mlx_state |= MLX_STATE_INTEN; 2599f6b84b08SMike Smith break; 2600f6b84b08SMike Smith } 2601f6b84b08SMike Smith } 2602f6b84b08SMike Smith 2603da8bb3a3SMike Smith /******************************************************************************** 2604da8bb3a3SMike Smith * Poll for firmware error codes during controller initialisation. 2605da8bb3a3SMike Smith * Returns 0 if initialisation is complete, 1 if still in progress but no 2606da8bb3a3SMike Smith * error has been fetched, 2 if an error has been retrieved. 2607da8bb3a3SMike Smith */ 2608da8bb3a3SMike Smith static int 2609da8bb3a3SMike Smith mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 2610da8bb3a3SMike Smith { 2611da8bb3a3SMike Smith u_int8_t fwerror; 2612da8bb3a3SMike Smith static int initted = 0; 2613da8bb3a3SMike Smith 2614da8bb3a3SMike Smith debug_called(2); 2615da8bb3a3SMike Smith 2616da8bb3a3SMike Smith /* first time around, clear any hardware completion status */ 2617da8bb3a3SMike Smith if (!initted) { 2618da8bb3a3SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); 2619da8bb3a3SMike Smith DELAY(1000); 2620da8bb3a3SMike Smith initted = 1; 2621da8bb3a3SMike Smith } 2622da8bb3a3SMike Smith 2623da8bb3a3SMike Smith /* init in progress? */ 2624da8bb3a3SMike Smith if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY)) 2625da8bb3a3SMike Smith return(0); 2626da8bb3a3SMike Smith 2627da8bb3a3SMike Smith /* test error value */ 2628da8bb3a3SMike Smith fwerror = MLX_V4_GET_FWERROR(sc); 2629da8bb3a3SMike Smith if (!(fwerror & MLX_V4_FWERROR_PEND)) 2630da8bb3a3SMike Smith return(1); 2631da8bb3a3SMike Smith 2632da8bb3a3SMike Smith /* mask status pending bit, fetch status */ 2633da8bb3a3SMike Smith *error = fwerror & ~MLX_V4_FWERROR_PEND; 2634da8bb3a3SMike Smith *param1 = MLX_V4_GET_FWERROR_PARAM1(sc); 2635da8bb3a3SMike Smith *param2 = MLX_V4_GET_FWERROR_PARAM2(sc); 2636da8bb3a3SMike Smith 2637da8bb3a3SMike Smith /* acknowledge */ 2638da8bb3a3SMike Smith MLX_V4_PUT_FWERROR(sc, 0); 2639da8bb3a3SMike Smith 2640da8bb3a3SMike Smith return(2); 2641da8bb3a3SMike Smith } 2642f6b84b08SMike Smith 2643f6b84b08SMike Smith /******************************************************************************** 2644f6b84b08SMike Smith ******************************************************************************** 26455792b7feSMike Smith Type 5 interface accessor methods 26465792b7feSMike Smith ******************************************************************************** 26475792b7feSMike Smith ********************************************************************************/ 26485792b7feSMike Smith 26495792b7feSMike Smith /******************************************************************************** 26505792b7feSMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 26515792b7feSMike Smith * (the controller is not ready to take a command). 26525792b7feSMike Smith * 26535792b7feSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 26545792b7feSMike Smith */ 26555792b7feSMike Smith static int 26565792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 26575792b7feSMike Smith { 26585792b7feSMike Smith int i; 26595792b7feSMike Smith 2660da8bb3a3SMike Smith debug_called(2); 26615792b7feSMike Smith 26625792b7feSMike Smith /* ready for our command? */ 26635792b7feSMike Smith if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) { 26645792b7feSMike Smith /* copy mailbox data to window */ 26655792b7feSMike Smith for (i = 0; i < 13; i++) 26665792b7feSMike Smith MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 26675792b7feSMike Smith 26685792b7feSMike Smith /* post command */ 26695792b7feSMike Smith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD); 26705792b7feSMike Smith return(1); 26715792b7feSMike Smith } 26725792b7feSMike Smith return(0); 26735792b7feSMike Smith } 26745792b7feSMike Smith 26755792b7feSMike Smith /******************************************************************************** 26765792b7feSMike Smith * See if a command has been completed, if so acknowledge its completion 26775792b7feSMike Smith * and recover the slot number and status code. 26785792b7feSMike Smith * 26795792b7feSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 26805792b7feSMike Smith */ 26815792b7feSMike Smith static int 26825792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 26835792b7feSMike Smith { 26845792b7feSMike Smith 2685da8bb3a3SMike Smith debug_called(2); 26865792b7feSMike Smith 26875792b7feSMike Smith /* status available? */ 26885792b7feSMike Smith if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) { 26895792b7feSMike Smith *slot = MLX_V5_GET_STATUS_IDENT(sc); /* get command identifier */ 26905792b7feSMike Smith *status = MLX_V5_GET_STATUS(sc); /* get status */ 26915792b7feSMike Smith 26925792b7feSMike Smith /* acknowledge completion */ 26935792b7feSMike Smith MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK); 26945792b7feSMike Smith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK); 26955792b7feSMike Smith return(1); 26965792b7feSMike Smith } 26975792b7feSMike Smith return(0); 26985792b7feSMike Smith } 26995792b7feSMike Smith 27005792b7feSMike Smith /******************************************************************************** 27015792b7feSMike Smith * Enable/disable interrupts as requested. 27025792b7feSMike Smith * 27035792b7feSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 27045792b7feSMike Smith */ 27055792b7feSMike Smith static void 27065792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action) 27075792b7feSMike Smith { 2708da8bb3a3SMike Smith debug_called(1); 27095792b7feSMike Smith 27105792b7feSMike Smith switch(action) { 27115792b7feSMike Smith case MLX_INTACTION_DISABLE: 2712da8bb3a3SMike Smith MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT); 27135792b7feSMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 27145792b7feSMike Smith break; 27155792b7feSMike Smith case MLX_INTACTION_ENABLE: 2716da8bb3a3SMike Smith MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT); 27175792b7feSMike Smith sc->mlx_state |= MLX_STATE_INTEN; 27185792b7feSMike Smith break; 27195792b7feSMike Smith } 27205792b7feSMike Smith } 27215792b7feSMike Smith 2722da8bb3a3SMike Smith /******************************************************************************** 2723da8bb3a3SMike Smith * Poll for firmware error codes during controller initialisation. 2724da8bb3a3SMike Smith * Returns 0 if initialisation is complete, 1 if still in progress but no 2725da8bb3a3SMike Smith * error has been fetched, 2 if an error has been retrieved. 2726da8bb3a3SMike Smith */ 2727da8bb3a3SMike Smith static int 2728da8bb3a3SMike Smith mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 2729da8bb3a3SMike Smith { 2730da8bb3a3SMike Smith u_int8_t fwerror; 2731da8bb3a3SMike Smith static int initted = 0; 2732da8bb3a3SMike Smith 2733da8bb3a3SMike Smith debug_called(2); 2734da8bb3a3SMike Smith 2735da8bb3a3SMike Smith /* first time around, clear any hardware completion status */ 2736da8bb3a3SMike Smith if (!initted) { 2737da8bb3a3SMike Smith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK); 2738da8bb3a3SMike Smith DELAY(1000); 2739da8bb3a3SMike Smith initted = 1; 2740da8bb3a3SMike Smith } 2741da8bb3a3SMike Smith 2742da8bb3a3SMike Smith /* init in progress? */ 2743da8bb3a3SMike Smith if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE) 2744da8bb3a3SMike Smith return(0); 2745da8bb3a3SMike Smith 2746da8bb3a3SMike Smith /* test for error value */ 2747da8bb3a3SMike Smith fwerror = MLX_V5_GET_FWERROR(sc); 2748da8bb3a3SMike Smith if (!(fwerror & MLX_V5_FWERROR_PEND)) 2749da8bb3a3SMike Smith return(1); 2750da8bb3a3SMike Smith 2751da8bb3a3SMike Smith /* mask status pending bit, fetch status */ 2752da8bb3a3SMike Smith *error = fwerror & ~MLX_V5_FWERROR_PEND; 2753da8bb3a3SMike Smith *param1 = MLX_V5_GET_FWERROR_PARAM1(sc); 2754da8bb3a3SMike Smith *param2 = MLX_V5_GET_FWERROR_PARAM2(sc); 2755da8bb3a3SMike Smith 2756da8bb3a3SMike Smith /* acknowledge */ 2757da8bb3a3SMike Smith MLX_V5_PUT_FWERROR(sc, 0xff); 2758da8bb3a3SMike Smith 2759da8bb3a3SMike Smith return(2); 2760da8bb3a3SMike Smith } 27615792b7feSMike Smith 27625792b7feSMike Smith /******************************************************************************** 27635792b7feSMike Smith ******************************************************************************** 27641ac4b82bSMike Smith Debugging 27651ac4b82bSMike Smith ******************************************************************************** 27661ac4b82bSMike Smith ********************************************************************************/ 27671ac4b82bSMike Smith 27681ac4b82bSMike Smith /******************************************************************************** 27691ac4b82bSMike Smith * Return a status message describing (mc) 27701ac4b82bSMike Smith */ 27711ac4b82bSMike Smith static char *mlx_status_messages[] = { 27721ac4b82bSMike Smith "normal completion", /* 00 */ 27731ac4b82bSMike Smith "irrecoverable data error", /* 01 */ 27741ac4b82bSMike Smith "drive does not exist, or is offline", /* 02 */ 27751ac4b82bSMike Smith "attempt to write beyond end of drive", /* 03 */ 27761ac4b82bSMike Smith "bad data encountered", /* 04 */ 27771ac4b82bSMike Smith "invalid log entry request", /* 05 */ 27781ac4b82bSMike Smith "attempt to rebuild online drive", /* 06 */ 27791ac4b82bSMike Smith "new disk failed during rebuild", /* 07 */ 27801ac4b82bSMike Smith "invalid channel/target", /* 08 */ 27811ac4b82bSMike Smith "rebuild/check already in progress", /* 09 */ 27821ac4b82bSMike Smith "one or more disks are dead", /* 10 */ 27831ac4b82bSMike Smith "invalid or non-redundant drive", /* 11 */ 27841ac4b82bSMike Smith "channel is busy", /* 12 */ 27851ac4b82bSMike Smith "channel is not stopped", /* 13 */ 2786da8bb3a3SMike Smith "rebuild successfully terminated", /* 14 */ 2787da8bb3a3SMike Smith "unsupported command", /* 15 */ 2788da8bb3a3SMike Smith "check condition received", /* 16 */ 2789da8bb3a3SMike Smith "device is busy", /* 17 */ 2790da8bb3a3SMike Smith "selection or command timeout", /* 18 */ 2791da8bb3a3SMike Smith "command terminated abnormally", /* 19 */ 2792da8bb3a3SMike Smith "" 27931ac4b82bSMike Smith }; 27941ac4b82bSMike Smith 27951ac4b82bSMike Smith static struct 27961ac4b82bSMike Smith { 27971ac4b82bSMike Smith int command; 27981ac4b82bSMike Smith u_int16_t status; 27991ac4b82bSMike Smith int msg; 28001ac4b82bSMike Smith } mlx_messages[] = { 2801da8bb3a3SMike Smith {MLX_CMD_READSG, 0x0001, 1}, 2802da8bb3a3SMike Smith {MLX_CMD_READSG, 0x0002, 1}, 2803da8bb3a3SMike Smith {MLX_CMD_READSG, 0x0105, 3}, 2804da8bb3a3SMike Smith {MLX_CMD_READSG, 0x010c, 4}, 2805da8bb3a3SMike Smith {MLX_CMD_WRITESG, 0x0001, 1}, 2806da8bb3a3SMike Smith {MLX_CMD_WRITESG, 0x0002, 1}, 2807da8bb3a3SMike Smith {MLX_CMD_WRITESG, 0x0105, 3}, 2808da8bb3a3SMike Smith {MLX_CMD_READSG_OLD, 0x0001, 1}, 2809da8bb3a3SMike Smith {MLX_CMD_READSG_OLD, 0x0002, 1}, 2810da8bb3a3SMike Smith {MLX_CMD_READSG_OLD, 0x0105, 3}, 2811da8bb3a3SMike Smith {MLX_CMD_WRITESG_OLD, 0x0001, 1}, 2812da8bb3a3SMike Smith {MLX_CMD_WRITESG_OLD, 0x0002, 1}, 2813da8bb3a3SMike Smith {MLX_CMD_WRITESG_OLD, 0x0105, 3}, 28141ac4b82bSMike Smith {MLX_CMD_LOGOP, 0x0105, 5}, 28151ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0002, 6}, 28161ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0004, 7}, 28171ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0105, 8}, 28181ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0106, 9}, 2819da8bb3a3SMike Smith {MLX_CMD_REBUILDASYNC, 0x0107, 14}, 28201ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0002, 10}, 28211ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0105, 11}, 28221ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0106, 9}, 28231ac4b82bSMike Smith {MLX_CMD_STOPCHANNEL, 0x0106, 12}, 28241ac4b82bSMike Smith {MLX_CMD_STOPCHANNEL, 0x0105, 8}, 28251ac4b82bSMike Smith {MLX_CMD_STARTCHANNEL, 0x0005, 13}, 28261ac4b82bSMike Smith {MLX_CMD_STARTCHANNEL, 0x0105, 8}, 2827da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x0002, 16}, 2828da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x0008, 17}, 2829da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x000e, 18}, 2830da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x000f, 19}, 2831da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x0105, 8}, 2832da8bb3a3SMike Smith 2833da8bb3a3SMike Smith {0, 0x0104, 14}, 28341ac4b82bSMike Smith {-1, 0, 0} 28351ac4b82bSMike Smith }; 28361ac4b82bSMike Smith 28371ac4b82bSMike Smith static char * 28381ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc) 28391ac4b82bSMike Smith { 28401ac4b82bSMike Smith static char unkmsg[80]; 28411ac4b82bSMike Smith int i; 28421ac4b82bSMike Smith 28431ac4b82bSMike Smith /* look up message in table */ 28441ac4b82bSMike Smith for (i = 0; mlx_messages[i].command != -1; i++) 2845da8bb3a3SMike Smith if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) && 2846466454bdSMike Smith (mc->mc_status == mlx_messages[i].status)) 28471ac4b82bSMike Smith return(mlx_status_messages[mlx_messages[i].msg]); 28481ac4b82bSMike Smith 28491ac4b82bSMike Smith sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]); 28501ac4b82bSMike Smith return(unkmsg); 28511ac4b82bSMike Smith } 28521ac4b82bSMike Smith 28531ac4b82bSMike Smith /******************************************************************************* 2854da8bb3a3SMike Smith * Print a string describing the controller (sc) 28551ac4b82bSMike Smith */ 28565792b7feSMike Smith static struct 28575792b7feSMike Smith { 28585792b7feSMike Smith int hwid; 28595792b7feSMike Smith char *name; 28605792b7feSMike Smith } mlx_controller_names[] = { 28615792b7feSMike Smith {0x01, "960P/PD"}, 28625792b7feSMike Smith {0x02, "960PL"}, 28635792b7feSMike Smith {0x10, "960PG"}, 28645792b7feSMike Smith {0x11, "960PJ"}, 28659eee27f1SMike Smith {0x12, "960PR"}, 28669eee27f1SMike Smith {0x13, "960PT"}, 28679eee27f1SMike Smith {0x14, "960PTL0"}, 28689eee27f1SMike Smith {0x15, "960PRL"}, 28699eee27f1SMike Smith {0x16, "960PTL1"}, 28709eee27f1SMike Smith {0x20, "1164PVX"}, 28715792b7feSMike Smith {-1, NULL} 28725792b7feSMike Smith }; 28735792b7feSMike Smith 28749eee27f1SMike Smith static void 28759eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc) 28761ac4b82bSMike Smith { 28771ac4b82bSMike Smith static char buf[80]; 28785792b7feSMike Smith char *model; 28799eee27f1SMike Smith int i; 28801ac4b82bSMike Smith 28815792b7feSMike Smith for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) { 28829eee27f1SMike Smith if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) { 28835792b7feSMike Smith model = mlx_controller_names[i].name; 28841ac4b82bSMike Smith break; 28851ac4b82bSMike Smith } 28865792b7feSMike Smith } 28875792b7feSMike Smith if (model == NULL) { 28889eee27f1SMike Smith sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff); 28895792b7feSMike Smith model = buf; 28905792b7feSMike Smith } 2891da8bb3a3SMike Smith device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n", 28929eee27f1SMike Smith model, 28939eee27f1SMike Smith sc->mlx_enq2->me_actual_channels, 28949eee27f1SMike Smith sc->mlx_enq2->me_actual_channels > 1 ? "s" : "", 28959eee27f1SMike Smith sc->mlx_enq2->me_firmware_id & 0xff, 28969eee27f1SMike Smith (sc->mlx_enq2->me_firmware_id >> 8) & 0xff, 28979eee27f1SMike Smith (sc->mlx_enq2->me_firmware_id >> 24) & 0xff, 2898b9256fe3SMike Smith (sc->mlx_enq2->me_firmware_id >> 16) & 0xff, 28999eee27f1SMike Smith sc->mlx_enq2->me_mem_size / (1024 * 1024)); 29009eee27f1SMike Smith 29019eee27f1SMike Smith if (bootverbose) { 29029eee27f1SMike Smith device_printf(sc->mlx_dev, " Hardware ID 0x%08x\n", sc->mlx_enq2->me_hardware_id); 29039eee27f1SMike Smith device_printf(sc->mlx_dev, " Firmware ID 0x%08x\n", sc->mlx_enq2->me_firmware_id); 29049eee27f1SMike Smith device_printf(sc->mlx_dev, " Configured/Actual channels %d/%d\n", sc->mlx_enq2->me_configured_channels, 29059eee27f1SMike Smith sc->mlx_enq2->me_actual_channels); 29069eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Targets %d\n", sc->mlx_enq2->me_max_targets); 29079eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Tags %d\n", sc->mlx_enq2->me_max_tags); 29089eee27f1SMike Smith device_printf(sc->mlx_dev, " Max System Drives %d\n", sc->mlx_enq2->me_max_sys_drives); 29099eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Arms %d\n", sc->mlx_enq2->me_max_arms); 29109eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Spans %d\n", sc->mlx_enq2->me_max_spans); 29119eee27f1SMike Smith device_printf(sc->mlx_dev, " DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size, 29129eee27f1SMike Smith sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size); 29139eee27f1SMike Smith device_printf(sc->mlx_dev, " DRAM type %d\n", sc->mlx_enq2->me_mem_type); 29149eee27f1SMike Smith device_printf(sc->mlx_dev, " Clock Speed %dns\n", sc->mlx_enq2->me_clock_speed); 29159eee27f1SMike Smith device_printf(sc->mlx_dev, " Hardware Speed %dns\n", sc->mlx_enq2->me_hardware_speed); 29169eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Commands %d\n", sc->mlx_enq2->me_max_commands); 29179eee27f1SMike Smith device_printf(sc->mlx_dev, " Max SG Entries %d\n", sc->mlx_enq2->me_max_sg); 29189eee27f1SMike Smith device_printf(sc->mlx_dev, " Max DP %d\n", sc->mlx_enq2->me_max_dp); 29199eee27f1SMike Smith device_printf(sc->mlx_dev, " Max IOD %d\n", sc->mlx_enq2->me_max_iod); 29209eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Comb %d\n", sc->mlx_enq2->me_max_comb); 29219eee27f1SMike Smith device_printf(sc->mlx_dev, " Latency %ds\n", sc->mlx_enq2->me_latency); 29229eee27f1SMike Smith device_printf(sc->mlx_dev, " SCSI Timeout %ds\n", sc->mlx_enq2->me_scsi_timeout); 29239eee27f1SMike Smith device_printf(sc->mlx_dev, " Min Free Lines %d\n", sc->mlx_enq2->me_min_freelines); 29249eee27f1SMike Smith device_printf(sc->mlx_dev, " Rate Constant %d\n", sc->mlx_enq2->me_rate_const); 29259eee27f1SMike Smith device_printf(sc->mlx_dev, " MAXBLK %d\n", sc->mlx_enq2->me_maxblk); 29269eee27f1SMike Smith device_printf(sc->mlx_dev, " Blocking Factor %d sectors\n", sc->mlx_enq2->me_blocking_factor); 29279eee27f1SMike Smith device_printf(sc->mlx_dev, " Cache Line Size %d blocks\n", sc->mlx_enq2->me_cacheline); 29289eee27f1SMike Smith device_printf(sc->mlx_dev, " SCSI Capability %s%dMHz, %d bit\n", 29299eee27f1SMike Smith sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "", 29309eee27f1SMike Smith (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10, 29319eee27f1SMike Smith 8 << (sc->mlx_enq2->me_scsi_cap & 0x3)); 29329eee27f1SMike Smith device_printf(sc->mlx_dev, " Firmware Build Number %d\n", sc->mlx_enq2->me_firmware_build); 29339eee27f1SMike Smith device_printf(sc->mlx_dev, " Fault Management Type %d\n", sc->mlx_enq2->me_fault_mgmt_type); 29349eee27f1SMike Smith device_printf(sc->mlx_dev, " Features %b\n", sc->mlx_enq2->me_firmware_features, 29359eee27f1SMike Smith "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n"); 29369eee27f1SMike Smith 29379eee27f1SMike Smith } 29381ac4b82bSMike Smith } 29391ac4b82bSMike Smith 2940da8bb3a3SMike Smith /******************************************************************************* 2941da8bb3a3SMike Smith * Emit a string describing the firmware handshake status code, and return a flag 2942da8bb3a3SMike Smith * indicating whether the code represents a fatal error. 2943da8bb3a3SMike Smith * 2944da8bb3a3SMike Smith * Error code interpretations are from the Linux driver, and don't directly match 2945da8bb3a3SMike Smith * the messages printed by Mylex's BIOS. This may change if documentation on the 2946da8bb3a3SMike Smith * codes is forthcoming. 2947da8bb3a3SMike Smith */ 2948da8bb3a3SMike Smith static int 2949da8bb3a3SMike Smith mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2) 2950da8bb3a3SMike Smith { 2951da8bb3a3SMike Smith switch(error) { 2952da8bb3a3SMike Smith case 0x00: 2953da8bb3a3SMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1); 2954da8bb3a3SMike Smith break; 2955da8bb3a3SMike Smith case 0x08: 2956da8bb3a3SMike Smith /* we could be neater about this and give some indication when we receive more of them */ 2957da8bb3a3SMike Smith if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) { 2958da8bb3a3SMike Smith device_printf(sc->mlx_dev, "spinning up drives...\n"); 2959da8bb3a3SMike Smith sc->mlx_flags |= MLX_SPINUP_REPORTED; 2960da8bb3a3SMike Smith } 2961da8bb3a3SMike Smith break; 2962da8bb3a3SMike Smith case 0x30: 2963da8bb3a3SMike Smith device_printf(sc->mlx_dev, "configuration checksum error\n"); 2964da8bb3a3SMike Smith break; 2965da8bb3a3SMike Smith case 0x60: 2966da8bb3a3SMike Smith device_printf(sc->mlx_dev, "mirror race recovery failed\n"); 2967da8bb3a3SMike Smith break; 2968da8bb3a3SMike Smith case 0x70: 2969da8bb3a3SMike Smith device_printf(sc->mlx_dev, "mirror race recovery in progress\n"); 2970da8bb3a3SMike Smith break; 2971da8bb3a3SMike Smith case 0x90: 2972da8bb3a3SMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1); 2973da8bb3a3SMike Smith break; 2974da8bb3a3SMike Smith case 0xa0: 2975da8bb3a3SMike Smith device_printf(sc->mlx_dev, "logical drive installation aborted\n"); 2976da8bb3a3SMike Smith break; 2977da8bb3a3SMike Smith case 0xb0: 2978da8bb3a3SMike Smith device_printf(sc->mlx_dev, "mirror race on a critical system drive\n"); 2979da8bb3a3SMike Smith break; 2980da8bb3a3SMike Smith case 0xd0: 2981da8bb3a3SMike Smith device_printf(sc->mlx_dev, "new controller configuration found\n"); 2982da8bb3a3SMike Smith break; 2983da8bb3a3SMike Smith case 0xf0: 2984da8bb3a3SMike Smith device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n"); 2985da8bb3a3SMike Smith return(1); 2986da8bb3a3SMike Smith default: 2987da8bb3a3SMike Smith device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2); 2988da8bb3a3SMike Smith break; 2989da8bb3a3SMike Smith } 2990da8bb3a3SMike Smith return(0); 2991da8bb3a3SMike Smith } 2992da8bb3a3SMike Smith 29931ac4b82bSMike Smith /******************************************************************************** 29941ac4b82bSMike Smith ******************************************************************************** 29951ac4b82bSMike Smith Utility Functions 29961ac4b82bSMike Smith ******************************************************************************** 29971ac4b82bSMike Smith ********************************************************************************/ 29981ac4b82bSMike Smith 29991ac4b82bSMike Smith /******************************************************************************** 30001ac4b82bSMike Smith * Find the disk whose unit number is (unit) on this controller 30011ac4b82bSMike Smith */ 30021ac4b82bSMike Smith static struct mlx_sysdrive * 30031ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit) 30041ac4b82bSMike Smith { 30051ac4b82bSMike Smith int i; 30061ac4b82bSMike Smith 30071ac4b82bSMike Smith /* search system drives */ 30081ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 30091ac4b82bSMike Smith /* is this one attached? */ 30101ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 30111ac4b82bSMike Smith /* is this the one? */ 30121ac4b82bSMike Smith if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 30131ac4b82bSMike Smith return(&sc->mlx_sysdrive[i]); 30141ac4b82bSMike Smith } 30151ac4b82bSMike Smith } 30161ac4b82bSMike Smith return(NULL); 30171ac4b82bSMike Smith } 3018