diff options
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/ic/mfi.c | 1946 | ||||
| -rw-r--r-- | sys/dev/ic/mfireg.h | 928 | ||||
| -rw-r--r-- | sys/dev/ic/mfivar.h | 148 | ||||
| -rw-r--r-- | sys/dev/pci/files.pci | 7 | ||||
| -rw-r--r-- | sys/dev/pci/mfi_pci.c | 156 |
5 files changed, 3184 insertions, 1 deletions
diff --git a/sys/dev/ic/mfi.c b/sys/dev/ic/mfi.c new file mode 100644 index 00000000000..fe340c4bec1 --- /dev/null +++ b/sys/dev/ic/mfi.c @@ -0,0 +1,1946 @@ +/* $OpenBSD: mfi.c,v 1.66 2006/11/28 23:59:45 dlg Exp $ */ +/* + * Copyright (c) 2006 Marco Peereboom <marco@peereboom.us> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* #include "bio.h" XXX */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/ioctl.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> + +#include <uvm/uvm_param.h> + +#include <machine/bus.h> + +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsi_spc.h> +#include <dev/scsipi/scsipi_disk.h> +#include <dev/scsipi/scsi_disk.h> +#include <dev/scsipi/scsiconf.h> + +#include <dev/ic/mfireg.h> +#include <dev/ic/mfivar.h> + +#if NBIO > 0 +#include <dev/biovar.h> +#include <sys/sensors.h> +#endif /* NBIO > 0 */ + +#ifdef MFI_DEBUG +uint32_t mfi_debug = 0 +/* | MFI_D_CMD */ +/* | MFI_D_INTR */ +/* | MFI_D_MISC */ +/* | MFI_D_DMA */ + | MFI_D_IOCTL +/* | MFI_D_RW */ +/* | MFI_D_MEM */ +/* | MFI_D_CCB */ + ; +#endif + +void mfi_scsipi_request(struct scsipi_channel *, + scsipi_adapter_req_t, void *); +int mfi_scsi_ioctl(struct scsipi_channel *, u_long, caddr_t, int, + struct proc *); +void mfiminphys(struct buf *bp); + +struct mfi_ccb *mfi_get_ccb(struct mfi_softc *); +void mfi_put_ccb(struct mfi_ccb *); +int mfi_init_ccb(struct mfi_softc *); + +struct mfi_mem *mfi_allocmem(struct mfi_softc *, size_t); +void mfi_freemem(struct mfi_softc *, struct mfi_mem *); + +int mfi_transition_firmware(struct mfi_softc *); +int mfi_initialize_firmware(struct mfi_softc *); +int mfi_get_info(struct mfi_softc *); +uint32_t mfi_read(struct mfi_softc *, bus_size_t); +void mfi_write(struct mfi_softc *, bus_size_t, uint32_t); +int mfi_poll(struct mfi_ccb *); +int mfi_despatch_cmd(struct mfi_ccb *); +int mfi_create_sgl(struct mfi_ccb *, int); + +/* commands */ +int mfi_scsi_ld(struct mfi_ccb *, struct scsipi_xfer *); +int mfi_scsi_io(struct mfi_ccb *, struct scsipi_xfer *, uint32_t, + uint32_t); +void mfi_scsi_xs_done(struct mfi_ccb *); +int mfi_mgmt(struct mfi_softc *, uint32_t, uint32_t, uint32_t, + void *, uint8_t *); +void mfi_mgmt_done(struct mfi_ccb *); + +#if NBIO > 0 +int mfi_ioctl(struct device *, u_long, caddr_t); +int mfi_ioctl_inq(struct mfi_softc *, struct bioc_inq *); +int mfi_ioctl_vol(struct mfi_softc *, struct bioc_vol *); +int mfi_ioctl_disk(struct mfi_softc *, struct bioc_disk *); +int mfi_ioctl_alarm(struct mfi_softc *, struct bioc_alarm *); +int mfi_ioctl_blink(struct mfi_softc *sc, struct bioc_blink *); +int mfi_ioctl_setstate(struct mfi_softc *, struct bioc_setstate *); +int mfi_bio_hs(struct mfi_softc *, int, int, void *); +int mfi_create_sensors(struct mfi_softc *); +void mfi_refresh_sensors(void *); +#endif /* NBIO > 0 */ + +struct mfi_ccb * +mfi_get_ccb(struct mfi_softc *sc) +{ + struct mfi_ccb *ccb; + int s; + + s = splbio(); + ccb = TAILQ_FIRST(&sc->sc_ccb_freeq); + if (ccb) { + TAILQ_REMOVE(&sc->sc_ccb_freeq, ccb, ccb_link); + ccb->ccb_state = MFI_CCB_READY; + } + splx(s); + + DNPRINTF(MFI_D_CCB, "%s: mfi_get_ccb: %p\n", DEVNAME(sc), ccb); + + return (ccb); +} + +void +mfi_put_ccb(struct mfi_ccb *ccb) +{ + struct mfi_softc *sc = ccb->ccb_sc; + int s; + + DNPRINTF(MFI_D_CCB, "%s: mfi_put_ccb: %p\n", DEVNAME(sc), ccb); + + s = splbio(); + ccb->ccb_state = MFI_CCB_FREE; + ccb->ccb_xs = NULL; + ccb->ccb_flags = 0; + ccb->ccb_done = NULL; + ccb->ccb_direction = 0; + ccb->ccb_frame_size = 0; + ccb->ccb_extra_frames = 0; + ccb->ccb_sgl = NULL; + ccb->ccb_data = NULL; + ccb->ccb_len = 0; + TAILQ_INSERT_TAIL(&sc->sc_ccb_freeq, ccb, ccb_link); + splx(s); +} + +int +mfi_init_ccb(struct mfi_softc *sc) +{ + struct mfi_ccb *ccb; + uint32_t i; + int error; + + DNPRINTF(MFI_D_CCB, "%s: mfi_init_ccb\n", DEVNAME(sc)); + + sc->sc_ccb = malloc(sizeof(struct mfi_ccb) * sc->sc_max_cmds, + M_DEVBUF, M_WAITOK); + memset(sc->sc_ccb, 0, sizeof(struct mfi_ccb) * sc->sc_max_cmds); + + for (i = 0; i < sc->sc_max_cmds; i++) { + ccb = &sc->sc_ccb[i]; + + ccb->ccb_sc = sc; + + /* select i'th frame */ + ccb->ccb_frame = (union mfi_frame *) + ((char*)MFIMEM_KVA(sc->sc_frames) + sc->sc_frames_size * i); + ccb->ccb_pframe = + MFIMEM_DVA(sc->sc_frames) + sc->sc_frames_size * i; + ccb->ccb_frame->mfr_header.mfh_context = i; + + /* select i'th sense */ + ccb->ccb_sense = (struct mfi_sense *) + ((char*)MFIMEM_KVA(sc->sc_sense) + MFI_SENSE_SIZE * i); + ccb->ccb_psense = + (MFIMEM_DVA(sc->sc_sense) + MFI_SENSE_SIZE * i); + + /* create a dma map for transfer */ + error = bus_dmamap_create(sc->sc_dmat, + MAXPHYS, sc->sc_max_sgl, MAXPHYS, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ccb->ccb_dmamap); + if (error) { + printf("%s: cannot create ccb dmamap (%d)\n", + DEVNAME(sc), error); + goto destroy; + } + + DNPRINTF(MFI_D_CCB, + "ccb(%d): %p frame: %#x (%#x) sense: %#x (%#x) map: %#x\n", + ccb->ccb_frame->mfr_header.mfh_context, ccb, + ccb->ccb_frame, ccb->ccb_pframe, + ccb->ccb_sense, ccb->ccb_psense, + ccb->ccb_dmamap); + + /* add ccb to queue */ + mfi_put_ccb(ccb); + } + + return (0); +destroy: + /* free dma maps and ccb memory */ + while (i) { + ccb = &sc->sc_ccb[i]; + bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); + i--; + } + + free(sc->sc_ccb, M_DEVBUF); + + return (1); +} + +uint32_t +mfi_read(struct mfi_softc *sc, bus_size_t r) +{ + uint32_t rv; + + bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4, + BUS_SPACE_BARRIER_READ); + rv = bus_space_read_4(sc->sc_iot, sc->sc_ioh, r); + + DNPRINTF(MFI_D_RW, "%s: mr 0x%x 0x08%x ", DEVNAME(sc), r, rv); + return (rv); +} + +void +mfi_write(struct mfi_softc *sc, bus_size_t r, uint32_t v) +{ + DNPRINTF(MFI_D_RW, "%s: mw 0x%x 0x%08x", DEVNAME(sc), r, v); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, r, v); + bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4, + BUS_SPACE_BARRIER_WRITE); +} + +struct mfi_mem * +mfi_allocmem(struct mfi_softc *sc, size_t size) +{ + struct mfi_mem *mm; + int nsegs; + + DNPRINTF(MFI_D_MEM, "%s: mfi_allocmem: %d\n", DEVNAME(sc), + size); + + mm = malloc(sizeof(struct mfi_mem), M_DEVBUF, M_NOWAIT); + if (mm == NULL) + return (NULL); + + memset(mm, 0, sizeof(struct mfi_mem)); + mm->am_size = size; + + if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &mm->am_map) != 0) + goto amfree; + + if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &mm->am_seg, 1, + &nsegs, BUS_DMA_NOWAIT) != 0) + goto destroy; + + if (bus_dmamem_map(sc->sc_dmat, &mm->am_seg, nsegs, size, &mm->am_kva, + BUS_DMA_NOWAIT) != 0) + goto free; + + if (bus_dmamap_load(sc->sc_dmat, mm->am_map, mm->am_kva, size, NULL, + BUS_DMA_NOWAIT) != 0) + goto unmap; + + DNPRINTF(MFI_D_MEM, " kva: %p dva: %p map: %p\n", + mm->am_kva, mm->am_map->dm_segs[0].ds_addr, mm->am_map); + + memset(mm->am_kva, 0, size); + return (mm); + +unmap: + bus_dmamem_unmap(sc->sc_dmat, mm->am_kva, size); +free: + bus_dmamem_free(sc->sc_dmat, &mm->am_seg, 1); +destroy: + bus_dmamap_destroy(sc->sc_dmat, mm->am_map); +amfree: + free(mm, M_DEVBUF); + + return (NULL); +} + +void +mfi_freemem(struct mfi_softc *sc, struct mfi_mem *mm) +{ + DNPRINTF(MFI_D_MEM, "%s: mfi_freemem: %p\n", DEVNAME(sc), mm); + + bus_dmamap_unload(sc->sc_dmat, mm->am_map); + bus_dmamem_unmap(sc->sc_dmat, mm->am_kva, mm->am_size); + bus_dmamem_free(sc->sc_dmat, &mm->am_seg, 1); + bus_dmamap_destroy(sc->sc_dmat, mm->am_map); + free(mm, M_DEVBUF); +} + +int +mfi_transition_firmware(struct mfi_softc *sc) +{ + int32_t fw_state, cur_state; + int max_wait, i; + + fw_state = mfi_read(sc, MFI_OMSG0) & MFI_STATE_MASK; + + DNPRINTF(MFI_D_CMD, "%s: mfi_transition_firmware: %#x\n", DEVNAME(sc), + fw_state); + + while (fw_state != MFI_STATE_READY) { + DNPRINTF(MFI_D_MISC, + "%s: waiting for firmware to become ready\n", + DEVNAME(sc)); + cur_state = fw_state; + switch (fw_state) { + case MFI_STATE_FAULT: + printf("%s: firmware fault\n", DEVNAME(sc)); + return (1); + case MFI_STATE_WAIT_HANDSHAKE: + mfi_write(sc, MFI_IDB, MFI_INIT_CLEAR_HANDSHAKE); + max_wait = 2; + break; + case MFI_STATE_OPERATIONAL: + mfi_write(sc, MFI_IDB, MFI_INIT_READY); + max_wait = 10; + break; + case MFI_STATE_UNDEFINED: + case MFI_STATE_BB_INIT: + max_wait = 2; + break; + case MFI_STATE_FW_INIT: + case MFI_STATE_DEVICE_SCAN: + case MFI_STATE_FLUSH_CACHE: + max_wait = 20; + break; + default: + printf("%s: unknown firmware state %d\n", + DEVNAME(sc), fw_state); + return (1); + } + for (i = 0; i < (max_wait * 10); i++) { + fw_state = mfi_read(sc, MFI_OMSG0) & MFI_STATE_MASK; + if (fw_state == cur_state) + DELAY(100000); + else + break; + } + if (fw_state == cur_state) { + printf("%s: firmware stuck in state %#x\n", + DEVNAME(sc), fw_state); + return (1); + } + } + + return (0); +} + +int +mfi_initialize_firmware(struct mfi_softc *sc) +{ + struct mfi_ccb *ccb; + struct mfi_init_frame *init; + struct mfi_init_qinfo *qinfo; + + DNPRINTF(MFI_D_MISC, "%s: mfi_initialize_firmware\n", DEVNAME(sc)); + + if ((ccb = mfi_get_ccb(sc)) == NULL) + return (1); + + init = &ccb->ccb_frame->mfr_init; + qinfo = (struct mfi_init_qinfo *)((uint8_t *)init + MFI_FRAME_SIZE); + + memset(qinfo, 0, sizeof *qinfo); + qinfo->miq_rq_entries = sc->sc_max_cmds + 1; + qinfo->miq_rq_addr_lo = htole32(MFIMEM_DVA(sc->sc_pcq) + + offsetof(struct mfi_prod_cons, mpc_reply_q)); + qinfo->miq_pi_addr_lo = htole32(MFIMEM_DVA(sc->sc_pcq) + + offsetof(struct mfi_prod_cons, mpc_producer)); + qinfo->miq_ci_addr_lo = htole32(MFIMEM_DVA(sc->sc_pcq) + + offsetof(struct mfi_prod_cons, mpc_consumer)); + + init->mif_header.mfh_cmd = MFI_CMD_INIT; + init->mif_header.mfh_data_len = sizeof *qinfo; + init->mif_qinfo_new_addr_lo = htole32(ccb->ccb_pframe + MFI_FRAME_SIZE); + + DNPRINTF(MFI_D_MISC, "%s: entries: %#x rq: %#x pi: %#x ci: %#x\n", + DEVNAME(sc), + qinfo->miq_rq_entries, qinfo->miq_rq_addr_lo, + qinfo->miq_pi_addr_lo, qinfo->miq_ci_addr_lo); + + if (mfi_poll(ccb)) { + printf("%s: mfi_initialize_firmware failed\n", DEVNAME(sc)); + return (1); + } + + mfi_put_ccb(ccb); + + return (0); +} + +int +mfi_get_info(struct mfi_softc *sc) +{ +#ifdef MFI_DEBUG + int i; +#endif + DNPRINTF(MFI_D_MISC, "%s: mfi_get_info\n", DEVNAME(sc)); + + if (mfi_mgmt(sc, MR_DCMD_CTRL_GET_INFO, MFI_DATA_IN, + sizeof(sc->sc_info), &sc->sc_info, NULL)) + return (1); + +#ifdef MFI_DEBUG + + for (i = 0; i < sc->sc_info.mci_image_component_count; i++) { + printf("%s: active FW %s Version %s date %s time %s\n", + DEVNAME(sc), + sc->sc_info.mci_image_component[i].mic_name, + sc->sc_info.mci_image_component[i].mic_version, + sc->sc_info.mci_image_component[i].mic_build_date, + sc->sc_info.mci_image_component[i].mic_build_time); + } + + for (i = 0; i < sc->sc_info.mci_pending_image_component_count; i++) { + printf("%s: pending FW %s Version %s date %s time %s\n", + DEVNAME(sc), + sc->sc_info.mci_pending_image_component[i].mic_name, + sc->sc_info.mci_pending_image_component[i].mic_version, + sc->sc_info.mci_pending_image_component[i].mic_build_date, + sc->sc_info.mci_pending_image_component[i].mic_build_time); + } + + printf("%s: max_arms %d max_spans %d max_arrs %d max_lds %d name %s\n", + DEVNAME(sc), + sc->sc_info.mci_max_arms, + sc->sc_info.mci_max_spans, + sc->sc_info.mci_max_arrays, + sc->sc_info.mci_max_lds, + sc->sc_info.mci_product_name); + + printf("%s: serial %s present %#x fw time %d max_cmds %d max_sg %d\n", + DEVNAME(sc), + sc->sc_info.mci_serial_number, + sc->sc_info.mci_hw_present, + sc->sc_info.mci_current_fw_time, + sc->sc_info.mci_max_cmds, + sc->sc_info.mci_max_sg_elements); + + printf("%s: max_rq %d lds_pres %d lds_deg %d lds_off %d pd_pres %d\n", + DEVNAME(sc), + sc->sc_info.mci_max_request_size, + sc->sc_info.mci_lds_present, + sc->sc_info.mci_lds_degraded, + sc->sc_info.mci_lds_offline, + sc->sc_info.mci_pd_present); + + printf("%s: pd_dsk_prs %d pd_dsk_pred_fail %d pd_dsk_fail %d\n", + DEVNAME(sc), + sc->sc_info.mci_pd_disks_present, + sc->sc_info.mci_pd_disks_pred_failure, + sc->sc_info.mci_pd_disks_failed); + + printf("%s: nvram %d mem %d flash %d\n", + DEVNAME(sc), + sc->sc_info.mci_nvram_size, + sc->sc_info.mci_memory_size, + sc->sc_info.mci_flash_size); + + printf("%s: ram_cor %d ram_uncor %d clus_all %d clus_act %d\n", + DEVNAME(sc), + sc->sc_info.mci_ram_correctable_errors, + sc->sc_info.mci_ram_uncorrectable_errors, + sc->sc_info.mci_cluster_allowed, + sc->sc_info.mci_cluster_active); + + printf("%s: max_strps_io %d raid_lvl %#x adapt_ops %#x ld_ops %#x\n", + DEVNAME(sc), + sc->sc_info.mci_max_strips_per_io, + sc->sc_info.mci_raid_levels, + sc->sc_info.mci_adapter_ops, + sc->sc_info.mci_ld_ops); + + printf("%s: strp_sz_min %d strp_sz_max %d pd_ops %#x pd_mix %#x\n", + DEVNAME(sc), + sc->sc_info.mci_stripe_sz_ops.min, + sc->sc_info.mci_stripe_sz_ops.max, + sc->sc_info.mci_pd_ops, + sc->sc_info.mci_pd_mix_support); + + printf("%s: ecc_bucket %d pckg_prop %s\n", + DEVNAME(sc), + sc->sc_info.mci_ecc_bucket_count, + sc->sc_info.mci_package_version); + + printf("%s: sq_nm %d prd_fail_poll %d intr_thrtl %d intr_thrtl_to %d\n", + DEVNAME(sc), + sc->sc_info.mci_properties.mcp_seq_num, + sc->sc_info.mci_properties.mcp_pred_fail_poll_interval, + sc->sc_info.mci_properties.mcp_intr_throttle_cnt, + sc->sc_info.mci_properties.mcp_intr_throttle_timeout); + + printf("%s: rbld_rate %d patr_rd_rate %d bgi_rate %d cc_rate %d\n", + DEVNAME(sc), + sc->sc_info.mci_properties.mcp_rebuild_rate, + sc->sc_info.mci_properties.mcp_patrol_read_rate, + sc->sc_info.mci_properties.mcp_bgi_rate, + sc->sc_info.mci_properties.mcp_cc_rate); + + printf("%s: rc_rate %d ch_flsh %d spin_cnt %d spin_dly %d clus_en %d\n", + DEVNAME(sc), + sc->sc_info.mci_properties.mcp_recon_rate, + sc->sc_info.mci_properties.mcp_cache_flush_interval, + sc->sc_info.mci_properties.mcp_spinup_drv_cnt, + sc->sc_info.mci_properties.mcp_spinup_delay, + sc->sc_info.mci_properties.mcp_cluster_enable); + + printf("%s: coerc %d alarm %d dis_auto_rbld %d dis_bat_wrn %d ecc %d\n", + DEVNAME(sc), + sc->sc_info.mci_properties.mcp_coercion_mode, + sc->sc_info.mci_properties.mcp_alarm_enable, + sc->sc_info.mci_properties.mcp_disable_auto_rebuild, + sc->sc_info.mci_properties.mcp_disable_battery_warn, + sc->sc_info.mci_properties.mcp_ecc_bucket_size); + + printf("%s: ecc_leak %d rest_hs %d exp_encl_dev %d\n", + DEVNAME(sc), + sc->sc_info.mci_properties.mcp_ecc_bucket_leak_rate, + sc->sc_info.mci_properties.mcp_restore_hotspare_on_insertion, + sc->sc_info.mci_properties.mcp_expose_encl_devices); + + printf("%s: vendor %#x device %#x subvendor %#x subdevice %#x\n", + DEVNAME(sc), + sc->sc_info.mci_pci.mip_vendor, + sc->sc_info.mci_pci.mip_device, + sc->sc_info.mci_pci.mip_subvendor, + sc->sc_info.mci_pci.mip_subdevice); + + printf("%s: type %#x port_count %d port_addr ", + DEVNAME(sc), + sc->sc_info.mci_host.mih_type, + sc->sc_info.mci_host.mih_port_count); + + for (i = 0; i < 8; i++) + printf("%.0llx ", sc->sc_info.mci_host.mih_port_addr[i]); + printf("\n"); + + printf("%s: type %.x port_count %d port_addr ", + DEVNAME(sc), + sc->sc_info.mci_device.mid_type, + sc->sc_info.mci_device.mid_port_count); + + for (i = 0; i < 8; i++) + printf("%.0llx ", sc->sc_info.mci_device.mid_port_addr[i]); + printf("\n"); +#endif /* MFI_DEBUG */ + + return (0); +} + +void +mfiminphys(struct buf *bp) +{ + DNPRINTF(MFI_D_MISC, "mfiminphys: %d\n", bp->b_bcount); + + /* XXX currently using MFI_MAXFER = MAXPHYS */ + if (bp->b_bcount > MFI_MAXFER) + bp->b_bcount = MFI_MAXFER; + minphys(bp); +} + +int +mfi_attach(struct mfi_softc *sc) +{ + struct scsipi_adapter *adapt = &sc->sc_adapt; + struct scsipi_channel *chan = &sc->sc_chan; + uint32_t status, frames; + int i; + + DNPRINTF(MFI_D_MISC, "%s: mfi_attach\n", DEVNAME(sc)); + + if (mfi_transition_firmware(sc)) + return (1); + + TAILQ_INIT(&sc->sc_ccb_freeq); + + /* rw_init(&sc->sc_lock, "mfi_lock"); XXX */ + + status = mfi_read(sc, MFI_OMSG0); + sc->sc_max_cmds = status & MFI_STATE_MAXCMD_MASK; + sc->sc_max_sgl = (status & MFI_STATE_MAXSGL_MASK) >> 16; + DNPRINTF(MFI_D_MISC, "%s: max commands: %u, max sgl: %u\n", + DEVNAME(sc), sc->sc_max_cmds, sc->sc_max_sgl); + + /* consumer/producer and reply queue memory */ + sc->sc_pcq = mfi_allocmem(sc, (sizeof(uint32_t) * sc->sc_max_cmds) + + sizeof(struct mfi_prod_cons)); + if (sc->sc_pcq == NULL) { + aprint_error("%s: unable to allocate reply queue memory\n", + DEVNAME(sc)); + goto nopcq; + } + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_pcq), 0, + sizeof(uint32_t) * sc->sc_max_cmds + sizeof(struct mfi_prod_cons), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* frame memory */ + /* we are not doing 64 bit IO so only calculate # of 32 bit frames */ + frames = (sizeof(struct mfi_sg32) * sc->sc_max_sgl + + MFI_FRAME_SIZE - 1) / MFI_FRAME_SIZE + 1; + sc->sc_frames_size = frames * MFI_FRAME_SIZE; + sc->sc_frames = mfi_allocmem(sc, sc->sc_frames_size * sc->sc_max_cmds); + if (sc->sc_frames == NULL) { + aprint_error("%s: unable to allocate frame memory\n", + DEVNAME(sc)); + goto noframe; + } + /* XXX hack, fix this */ + if (MFIMEM_DVA(sc->sc_frames) & 0x3f) { + aprint_error("%s: improper frame alignment (%#llx) FIXME\n", + DEVNAME(sc), (long long int)MFIMEM_DVA(sc->sc_frames)); + goto noframe; + } + + /* sense memory */ + sc->sc_sense = mfi_allocmem(sc, sc->sc_max_cmds * MFI_SENSE_SIZE); + if (sc->sc_sense == NULL) { + aprint_error("%s: unable to allocate sense memory\n", + DEVNAME(sc)); + goto nosense; + } + + /* now that we have all memory bits go initialize ccbs */ + if (mfi_init_ccb(sc)) { + aprint_error("%s: could not init ccb list\n", DEVNAME(sc)); + goto noinit; + } + + /* kickstart firmware with all addresses and pointers */ + if (mfi_initialize_firmware(sc)) { + aprint_error("%s: could not initialize firmware\n", + DEVNAME(sc)); + goto noinit; + } + + if (mfi_get_info(sc)) { + aprint_error("%s: could not retrieve controller information\n", + DEVNAME(sc)); + goto noinit; + } + + aprint_normal("%s: logical drives %d, version %s, %dMB RAM\n", + DEVNAME(sc), + sc->sc_info.mci_lds_present, + sc->sc_info.mci_package_version, + sc->sc_info.mci_memory_size); + + sc->sc_ld_cnt = sc->sc_info.mci_lds_present; + sc->sc_max_ld = sc->sc_ld_cnt; + for (i = 0; i < sc->sc_ld_cnt; i++) + sc->sc_ld[i].ld_present = 1; + + memset(adapt, 0, sizeof(*adapt)); + adapt->adapt_dev = &sc->sc_dev; + adapt->adapt_nchannels = 1; + if (sc->sc_ld_cnt) + adapt->adapt_openings = sc->sc_max_cmds / sc->sc_ld_cnt; + else + adapt->adapt_openings = sc->sc_max_cmds; + adapt->adapt_max_periph = adapt->adapt_openings; + adapt->adapt_request = mfi_scsipi_request; + adapt->adapt_minphys = mfiminphys; + adapt->adapt_ioctl = mfi_scsi_ioctl; + + memset(chan, 0, sizeof(*chan)); + chan->chan_adapter = adapt; + chan->chan_bustype = &scsi_bustype; + chan->chan_channel = 0; + chan->chan_flags = 0; + chan->chan_nluns = 8; + chan->chan_ntargets = MFI_MAX_LD; + chan->chan_id = MFI_MAX_LD; + + (void) config_found(&sc->sc_dev, &sc->sc_chan, scsiprint); + + /* enable interrupts */ + mfi_write(sc, MFI_OMSK, MFI_ENABLE_INTR); + +#if NBIO > 0 + if (bio_register(&sc->sc_dev, mfi_ioctl) != 0) + panic("%s: controller registration failed", DEVNAME(sc)); + else + sc->sc_ioctl = mfi_ioctl; + + if (mfi_create_sensors(sc) != 0) + aprint_error("%s: unable to create sensors\n", DEVNAME(sc)); +#endif /* NBIO > 0 */ + + return (0); +noinit: + mfi_freemem(sc, sc->sc_sense); +nosense: + mfi_freemem(sc, sc->sc_frames); +noframe: + mfi_freemem(sc, sc->sc_pcq); +nopcq: + return (1); +} + +int +mfi_despatch_cmd(struct mfi_ccb *ccb) +{ + struct mfi_softc *sc = ccb->ccb_sc; + DNPRINTF(MFI_D_CMD, "%s: mfi_despatch_cmd\n", DEVNAME(sc)); + + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_frames), + ccb->ccb_pframe - MFIMEM_DVA(sc->sc_frames), + sc->sc_frames_size, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_sense), + ccb->ccb_psense - MFIMEM_DVA(sc->sc_sense), + MFI_SENSE_SIZE, BUS_DMASYNC_PREREAD); + + mfi_write(ccb->ccb_sc, MFI_IQP, htole32((ccb->ccb_pframe >> 3) | + ccb->ccb_extra_frames)); + + return(0); +} + +int +mfi_poll(struct mfi_ccb *ccb) +{ + struct mfi_softc *sc = ccb->ccb_sc; + struct mfi_frame_header *hdr; + int to = 0; + + DNPRINTF(MFI_D_CMD, "%s: mfi_poll\n", DEVNAME(sc)); + + hdr = &ccb->ccb_frame->mfr_header; + hdr->mfh_cmd_status = 0xff; + hdr->mfh_flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; + + mfi_despatch_cmd(ccb); + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_frames), + ccb->ccb_pframe - MFIMEM_DVA(sc->sc_frames), + sc->sc_frames_size, BUS_DMASYNC_POSTREAD); + + while (hdr->mfh_cmd_status == 0xff) { + delay(1000); + if (to++ > 5000) /* XXX 5 seconds busywait sucks */ + break; + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_frames), + ccb->ccb_pframe - MFIMEM_DVA(sc->sc_frames), + sc->sc_frames_size, BUS_DMASYNC_POSTREAD); + } + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_frames), + ccb->ccb_pframe - MFIMEM_DVA(sc->sc_frames), + sc->sc_frames_size, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + if (ccb->ccb_data != NULL) { + DNPRINTF(MFI_D_INTR, "%s: mfi_mgmt_done sync\n", + DEVNAME(sc)); + bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, + ccb->ccb_dmamap->dm_mapsize, + (ccb->ccb_direction & MFI_DATA_IN) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + + bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); + } + + if (hdr->mfh_cmd_status == 0xff) { + printf("%s: timeout on ccb %d\n", DEVNAME(sc), + hdr->mfh_context); + ccb->ccb_flags |= MFI_CCB_F_ERR; + return (1); + } + + return (0); +} + +int +mfi_intr(void *arg) +{ + struct mfi_softc *sc = arg; + struct mfi_prod_cons *pcq; + struct mfi_ccb *ccb; + uint32_t status, producer, consumer, ctx; + int claimed = 0; + + status = mfi_read(sc, MFI_OSTS); + if ((status & MFI_OSTS_INTR_VALID) == 0) + return (claimed); + /* write status back to acknowledge interrupt */ + mfi_write(sc, MFI_OSTS, status); + + DNPRINTF(MFI_D_INTR, "%s: mfi_intr %#x %#x\n", DEVNAME(sc), sc, pcq); + + pcq = MFIMEM_KVA(sc->sc_pcq); + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_pcq), 0, + sizeof(uint32_t) * sc->sc_max_cmds + sizeof(struct mfi_prod_cons), + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + producer = pcq->mpc_producer; + consumer = pcq->mpc_consumer; + + while (consumer != producer) { + DNPRINTF(MFI_D_INTR, "%s: mfi_intr pi %#x ci %#x\n", + DEVNAME(sc), producer, consumer); + + ctx = pcq->mpc_reply_q[consumer]; + pcq->mpc_reply_q[consumer] = MFI_INVALID_CTX; + if (ctx == MFI_INVALID_CTX) + printf("%s: invalid context, p: %d c: %d\n", + DEVNAME(sc), producer, consumer); + else { + /* XXX remove from queue and call scsi_done */ + ccb = &sc->sc_ccb[ctx]; + DNPRINTF(MFI_D_INTR, "%s: mfi_intr context %#x\n", + DEVNAME(sc), ctx); + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_frames), + ccb->ccb_pframe - MFIMEM_DVA(sc->sc_frames), + sc->sc_frames_size, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + ccb->ccb_done(ccb); + + claimed = 1; + } + consumer++; + if (consumer == (sc->sc_max_cmds + 1)) + consumer = 0; + } + + pcq->mpc_consumer = consumer; + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_pcq), 0, + sizeof(uint32_t) * sc->sc_max_cmds + sizeof(struct mfi_prod_cons), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (claimed); +} + +int +mfi_scsi_io(struct mfi_ccb *ccb, struct scsipi_xfer *xs, uint32_t blockno, + uint32_t blockcnt) +{ + struct scsipi_periph *periph = xs->xs_periph; + struct mfi_io_frame *io; + + DNPRINTF(MFI_D_CMD, "%s: mfi_scsi_io: %d\n", + periph->periph_channel->chan_adapter->adapt_dev->dv_xname, + periph->periph_target); + + if (!xs->data) + return (1); + + io = &ccb->ccb_frame->mfr_io; + if (xs->xs_control & XS_CTL_DATA_IN) { + io->mif_header.mfh_cmd = MFI_CMD_LD_READ; + ccb->ccb_direction = MFI_DATA_IN; + } else { + io->mif_header.mfh_cmd = MFI_CMD_LD_WRITE; + ccb->ccb_direction = MFI_DATA_OUT; + } + io->mif_header.mfh_target_id = periph->periph_target; + io->mif_header.mfh_timeout = 0; + io->mif_header.mfh_flags = 0; + io->mif_header.mfh_sense_len = MFI_SENSE_SIZE; + io->mif_header.mfh_data_len= blockcnt; + io->mif_lba_hi = 0; + io->mif_lba_lo = blockno; + io->mif_sense_addr_lo = htole32(ccb->ccb_psense); + io->mif_sense_addr_hi = 0; + + ccb->ccb_done = mfi_scsi_xs_done; + ccb->ccb_xs = xs; + ccb->ccb_frame_size = MFI_IO_FRAME_SIZE; + ccb->ccb_sgl = &io->mif_sgl; + ccb->ccb_data = xs->data; + ccb->ccb_len = xs->datalen; + + if (mfi_create_sgl(ccb, xs->xs_control & XS_CTL_NOSLEEP) ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK) + return (1); + + return (0); +} + +void +mfi_scsi_xs_done(struct mfi_ccb *ccb) +{ + struct scsipi_xfer *xs = ccb->ccb_xs; + struct mfi_softc *sc = ccb->ccb_sc; + struct mfi_frame_header *hdr = &ccb->ccb_frame->mfr_header; + + DNPRINTF(MFI_D_INTR, "%s: mfi_scsi_xs_done %#x %#x\n", + DEVNAME(sc), ccb, ccb->ccb_frame); + + if (xs->data != NULL) { + DNPRINTF(MFI_D_INTR, "%s: mfi_scsi_xs_done sync\n", + DEVNAME(sc)); + bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, + ccb->ccb_dmamap->dm_mapsize, + (xs->xs_control & XS_CTL_DATA_IN) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + + bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); + } + + if (hdr->mfh_cmd_status != MFI_STAT_OK) { + xs->error = XS_DRIVER_STUFFUP; + DNPRINTF(MFI_D_INTR, "%s: mfi_scsi_xs_done stuffup %#x\n", + DEVNAME(sc), hdr->mfh_cmd_status); + + if (hdr->mfh_scsi_status != 0) { + bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_sense), + ccb->ccb_psense - MFIMEM_DVA(sc->sc_sense), + MFI_SENSE_SIZE, BUS_DMASYNC_POSTREAD); + DNPRINTF(MFI_D_INTR, + "%s: mfi_scsi_xs_done sense %#x %x %x\n", + DEVNAME(sc), hdr->mfh_scsi_status, + &xs->sense, ccb->ccb_sense); + memset(&xs->sense, 0, sizeof(xs->sense)); + memcpy(&xs->sense, ccb->ccb_sense, + sizeof(struct scsi_sense_data)); + xs->error = XS_SENSE; + } + } else { + xs->error = XS_NOERROR; + xs->status = SCSI_OK; + xs->resid = 0; + } + + mfi_put_ccb(ccb); + scsipi_done(xs); +} + +int +mfi_scsi_ld(struct mfi_ccb *ccb, struct scsipi_xfer *xs) +{ + struct mfi_pass_frame *pf; + struct scsipi_periph *periph = xs->xs_periph; + + DNPRINTF(MFI_D_CMD, "%s: mfi_scsi_ld: %d\n", + periph->periph_channel->chan_adapter->adapt_dev->dv_xname, + periph->periph_target); + + pf = &ccb->ccb_frame->mfr_pass; + pf->mpf_header.mfh_cmd = MFI_CMD_LD_SCSI_IO; + pf->mpf_header.mfh_target_id = periph->periph_target; + pf->mpf_header.mfh_lun_id = 0; + pf->mpf_header.mfh_cdb_len = xs->cmdlen; + pf->mpf_header.mfh_timeout = 0; + pf->mpf_header.mfh_data_len= xs->datalen; /* XXX */ + pf->mpf_header.mfh_sense_len = MFI_SENSE_SIZE; + + pf->mpf_sense_addr_hi = 0; + pf->mpf_sense_addr_lo = htole32(ccb->ccb_psense); + + memset(pf->mpf_cdb, 0, 16); + memcpy(pf->mpf_cdb, &xs->cmdstore, xs->cmdlen); + + ccb->ccb_done = mfi_scsi_xs_done; + ccb->ccb_xs = xs; + ccb->ccb_frame_size = MFI_PASS_FRAME_SIZE; + ccb->ccb_sgl = &pf->mpf_sgl; + + if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) + ccb->ccb_direction = (xs->xs_control & XS_CTL_DATA_IN) ? + MFI_DATA_IN : MFI_DATA_OUT; + else + ccb->ccb_direction = MFI_DATA_NONE; + + if (xs->data) { + ccb->ccb_data = xs->data; + ccb->ccb_len = xs->datalen; + + if (mfi_create_sgl(ccb, xs->xs_control & XS_CTL_NOSLEEP) ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK) + return (1); + } + + return (0); +} + +void +mfi_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, + void *arg) +{ + struct scsipi_periph *periph; + struct scsipi_xfer *xs; + struct scsipi_adapter *adapt = chan->chan_adapter; + struct mfi_softc *sc = (void *) adapt->adapt_dev; + struct mfi_ccb *ccb; + struct scsi_rw_6 *rw; + struct scsipi_rw_10 *rwb; + uint32_t blockno, blockcnt; + uint8_t target; + uint8_t mbox[MFI_MBOX_SIZE]; + int s; + + DNPRINTF(MFI_D_CMD, "%s: mfi_scsipi_request req %d opcode: %#x\n", + DEVNAME(sc), req, xs->cmd->opcode); + + switch (req) { + case ADAPTER_REQ_GROW_RESOURCES: + /* Not supported. */ + return; + case ADAPTER_REQ_SET_XFER_MODE: + /* Not supported. */ + return; + case ADAPTER_REQ_RUN_XFER: + break; + } + + xs = arg; + periph = xs->xs_periph; + target = periph->periph_target; + + s = splbio(); + if (target >= MFI_MAX_LD || !sc->sc_ld[target].ld_present || + periph->periph_lun != 0) { + DNPRINTF(MFI_D_CMD, "%s: invalid target %d\n", + DEVNAME(sc), target); + xs->error = XS_SELTIMEOUT; + scsipi_done(xs); + splx(s); + return; + } + + if ((ccb = mfi_get_ccb(sc)) == NULL) { + DNPRINTF(MFI_D_CMD, "%s: mfi_scsipi_request no ccb\n", DEVNAME(sc)); + xs->error = XS_RESOURCE_SHORTAGE; + scsipi_done(xs); + splx(s); + return; + } + + switch (xs->cmd->opcode) { + /* IO path */ + case READ_10: + case WRITE_10: + rwb = (struct scsipi_rw_10 *)xs->cmd; + blockno = _4btol(rwb->addr); + blockcnt = _2btol(rwb->length); + if (mfi_scsi_io(ccb, xs, blockno, blockcnt)) { + mfi_put_ccb(ccb); + goto stuffup; + } + break; + + case SCSI_READ_6_COMMAND: + case SCSI_WRITE_6_COMMAND: + rw = (struct scsi_rw_6 *)xs->cmd; + blockno = _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff); + blockcnt = rw->length ? rw->length : 0x100; + if (mfi_scsi_io(ccb, xs, blockno, blockcnt)) { + mfi_put_ccb(ccb); + goto stuffup; + } + break; + + case SCSI_SYNCHRONIZE_CACHE_10: + mfi_put_ccb(ccb); /* we don't need this */ + + mbox[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE; + if (mfi_mgmt(sc, MR_DCMD_CTRL_CACHE_FLUSH, MFI_DATA_NONE, + 0, NULL, mbox)) + goto stuffup; + xs->error = XS_NOERROR; + xs->status = SCSI_OK; + xs->resid = 0; + scsipi_done(xs); + splx(s); + return; + /* NOTREACHED */ + + /* hand it of to the firmware and let it deal with it */ + case SCSI_TEST_UNIT_READY: + /* save off sd? after autoconf */ + if (!cold) /* XXX bogus */ + strlcpy(sc->sc_ld[target].ld_dev, sc->sc_dev.dv_xname, + sizeof(sc->sc_ld[target].ld_dev)); + /* FALLTHROUGH */ + + default: + if (mfi_scsi_ld(ccb, xs)) { + mfi_put_ccb(ccb); + goto stuffup; + } + break; + } + + DNPRINTF(MFI_D_CMD, "%s: start io %d\n", DEVNAME(sc), target); + + if (xs->xs_control & XS_CTL_POLL) { + if (mfi_poll(ccb)) { + /* XXX check for sense in ccb->ccb_sense? */ + printf("%s: mfi_scsipi_request poll failed\n", + DEVNAME(sc)); + mfi_put_ccb(ccb); + bzero(&xs->sense, sizeof(xs->sense)); + xs->sense.scsi_sense.response_code = + SSD_RCODE_VALID | SSD_RCODE_CURRENT; + xs->sense.scsi_sense.flags = SKEY_ILLEGAL_REQUEST; + xs->sense.scsi_sense.asc = 0x20; /* invalid opcode */ + xs->error = XS_SENSE; + xs->status = SCSI_CHECK; + } else { + DNPRINTF(MFI_D_DMA, + "%s: mfi_scsipi_request poll complete %d\n", + DEVNAME(sc), ccb->ccb_dmamap->dm_nsegs); + xs->error = XS_NOERROR; + xs->status = SCSI_OK; + xs->resid = 0; + } + mfi_put_ccb(ccb); + scsipi_done(xs); + splx(s); + return; + } + + mfi_despatch_cmd(ccb); + + DNPRINTF(MFI_D_DMA, "%s: mfi_scsipi_request queued %d\n", DEVNAME(sc), + ccb->ccb_dmamap->dm_nsegs); + + splx(s); + return; + +stuffup: + xs->error = XS_DRIVER_STUFFUP; + scsipi_done(xs); + splx(s); +} + +int +mfi_create_sgl(struct mfi_ccb *ccb, int flags) +{ + struct mfi_softc *sc = ccb->ccb_sc; + struct mfi_frame_header *hdr; + bus_dma_segment_t *sgd; + union mfi_sgl *sgl; + int error, i; + + DNPRINTF(MFI_D_DMA, "%s: mfi_create_sgl %#x\n", DEVNAME(sc), + ccb->ccb_data); + + if (!ccb->ccb_data) + return (1); + + error = bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap, + ccb->ccb_data, ccb->ccb_len, NULL, flags); + if (error) { + if (error == EFBIG) + printf("more than %d dma segs\n", + sc->sc_max_sgl); + else + printf("error %d loading dma map\n", error); + return (1); + } + + hdr = &ccb->ccb_frame->mfr_header; + sgl = ccb->ccb_sgl; + sgd = ccb->ccb_dmamap->dm_segs; + for (i = 0; i < ccb->ccb_dmamap->dm_nsegs; i++) { + sgl->sg32[i].addr = htole32(sgd[i].ds_addr); + sgl->sg32[i].len = htole32(sgd[i].ds_len); + DNPRINTF(MFI_D_DMA, "%s: addr: %#x len: %#x\n", + DEVNAME(sc), sgl->sg32[i].addr, sgl->sg32[i].len); + } + + if (ccb->ccb_direction == MFI_DATA_IN) { + hdr->mfh_flags |= MFI_FRAME_DIR_READ; + bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, + ccb->ccb_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); + } else { + hdr->mfh_flags |= MFI_FRAME_DIR_WRITE; + bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, + ccb->ccb_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE); + } + + hdr->mfh_sg_count = ccb->ccb_dmamap->dm_nsegs; + /* for 64 bit io make the sizeof a variable to hold whatever sg size */ + ccb->ccb_frame_size += sizeof(struct mfi_sg32) * + ccb->ccb_dmamap->dm_nsegs; + ccb->ccb_extra_frames = (ccb->ccb_frame_size - 1) / MFI_FRAME_SIZE; + + DNPRINTF(MFI_D_DMA, "%s: sg_count: %d frame_size: %d frames_size: %d" + " dm_nsegs: %d extra_frames: %d\n", + DEVNAME(sc), + hdr->mfh_sg_count, + ccb->ccb_frame_size, + sc->sc_frames_size, + ccb->ccb_dmamap->dm_nsegs, + ccb->ccb_extra_frames); + + return (0); +} + +int +mfi_mgmt(struct mfi_softc *sc, uint32_t opc, uint32_t dir, uint32_t len, + void *buf, uint8_t *mbox) +{ + struct mfi_ccb *ccb; + struct mfi_dcmd_frame *dcmd; + int rv = 1; + + DNPRINTF(MFI_D_MISC, "%s: mfi_mgmt %#x\n", DEVNAME(sc), opc); + + if ((ccb = mfi_get_ccb(sc)) == NULL) + return (rv); + + dcmd = &ccb->ccb_frame->mfr_dcmd; + memset(dcmd->mdf_mbox, 0, MFI_MBOX_SIZE); + dcmd->mdf_header.mfh_cmd = MFI_CMD_DCMD; + dcmd->mdf_header.mfh_timeout = 0; + + dcmd->mdf_opcode = opc; + dcmd->mdf_header.mfh_data_len = 0; + ccb->ccb_direction = dir; + ccb->ccb_done = mfi_mgmt_done; + + ccb->ccb_frame_size = MFI_DCMD_FRAME_SIZE; + + /* handle special opcodes */ + if (mbox) + memcpy(dcmd->mdf_mbox, mbox, MFI_MBOX_SIZE); + + if (dir != MFI_DATA_NONE) { + dcmd->mdf_header.mfh_data_len = len; + ccb->ccb_data = buf; + ccb->ccb_len = len; + ccb->ccb_sgl = &dcmd->mdf_sgl; + + if (mfi_create_sgl(ccb, BUS_DMA_WAITOK)) + goto done; + } + + if (cold) { + if (mfi_poll(ccb)) + goto done; + } else { + mfi_despatch_cmd(ccb); + + DNPRINTF(MFI_D_MISC, "%s: mfi_mgmt sleeping\n", DEVNAME(sc)); + while (ccb->ccb_state != MFI_CCB_DONE) + tsleep(ccb, PRIBIO, "mfi_mgmt", 0); + + if (ccb->ccb_flags & MFI_CCB_F_ERR) + goto done; + } + + rv = 0; + +done: + mfi_put_ccb(ccb); + return (rv); +} + +void +mfi_mgmt_done(struct mfi_ccb *ccb) +{ + struct mfi_softc *sc = ccb->ccb_sc; + struct mfi_frame_header *hdr = &ccb->ccb_frame->mfr_header; + + DNPRINTF(MFI_D_INTR, "%s: mfi_mgmt_done %#x %#x\n", + DEVNAME(sc), ccb, ccb->ccb_frame); + + if (ccb->ccb_data != NULL) { + DNPRINTF(MFI_D_INTR, "%s: mfi_mgmt_done sync\n", + DEVNAME(sc)); + bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, + ccb->ccb_dmamap->dm_mapsize, + (ccb->ccb_direction & MFI_DATA_IN) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + + bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); + } + + if (hdr->mfh_cmd_status != MFI_STAT_OK) + ccb->ccb_flags |= MFI_CCB_F_ERR; + + ccb->ccb_state = MFI_CCB_DONE; + + wakeup(ccb); +} + + +int +mfi_scsi_ioctl(struct scsipi_channel *chan, u_long cmd, caddr_t arg, + int flag, struct proc *p) +{ + return (ENOTTY); +} + +#if NBIO > 0 +int +mfi_ioctl(struct device *dev, u_long cmd, caddr_t addr) +{ + struct mfi_softc *sc = (struct mfi_softc *)dev; + int error = 0; + + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl ", DEVNAME(sc)); + + rw_enter_write(&sc->sc_lock); + + switch (cmd) { + case BIOCINQ: + DNPRINTF(MFI_D_IOCTL, "inq\n"); + error = mfi_ioctl_inq(sc, (struct bioc_inq *)addr); + break; + + case BIOCVOL: + DNPRINTF(MFI_D_IOCTL, "vol\n"); + error = mfi_ioctl_vol(sc, (struct bioc_vol *)addr); + break; + + case BIOCDISK: + DNPRINTF(MFI_D_IOCTL, "disk\n"); + error = mfi_ioctl_disk(sc, (struct bioc_disk *)addr); + break; + + case BIOCALARM: + DNPRINTF(MFI_D_IOCTL, "alarm\n"); + error = mfi_ioctl_alarm(sc, (struct bioc_alarm *)addr); + break; + + case BIOCBLINK: + DNPRINTF(MFI_D_IOCTL, "blink\n"); + error = mfi_ioctl_blink(sc, (struct bioc_blink *)addr); + break; + + case BIOCSETSTATE: + DNPRINTF(MFI_D_IOCTL, "setstate\n"); + error = mfi_ioctl_setstate(sc, (struct bioc_setstate *)addr); + break; + + default: + DNPRINTF(MFI_D_IOCTL, " invalid ioctl\n"); + error = EINVAL; + } + + rw_exit_write(&sc->sc_lock); + + return (error); +} + +int +mfi_ioctl_inq(struct mfi_softc *sc, struct bioc_inq *bi) +{ + struct mfi_conf *cfg; + int rv = EINVAL; + + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_inq\n", DEVNAME(sc)); + + if (mfi_get_info(sc)) { + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_inq failed\n", + DEVNAME(sc)); + return (EIO); + } + + /* get figures */ + cfg = malloc(sizeof *cfg, M_DEVBUF, M_WAITOK); + if (mfi_mgmt(sc, MD_DCMD_CONF_GET, MFI_DATA_IN, sizeof *cfg, cfg, NULL)) + goto freeme; + + strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev)); + bi->bi_novol = cfg->mfc_no_ld + cfg->mfc_no_hs; + bi->bi_nodisk = sc->sc_info.mci_pd_disks_present; + + rv = 0; +freeme: + free(cfg, M_DEVBUF); + return (rv); +} + +int +mfi_ioctl_vol(struct mfi_softc *sc, struct bioc_vol *bv) +{ + int i, per, rv = EINVAL; + uint8_t mbox[MFI_MBOX_SIZE]; + + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_vol %#x\n", + DEVNAME(sc), bv->bv_volid); + + if (mfi_mgmt(sc, MR_DCMD_LD_GET_LIST, MFI_DATA_IN, + sizeof(sc->sc_ld_list), &sc->sc_ld_list, NULL)) + goto done; + + i = bv->bv_volid; + mbox[0] = sc->sc_ld_list.mll_list[i].mll_ld.mld_target; + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_vol target %#x\n", + DEVNAME(sc), mbox[0]); + + if (mfi_mgmt(sc, MR_DCMD_LD_GET_INFO, MFI_DATA_IN, + sizeof(sc->sc_ld_details), &sc->sc_ld_details, mbox)) + goto done; + + if (bv->bv_volid >= sc->sc_ld_list.mll_no_ld) { + /* go do hotspares */ + rv = mfi_bio_hs(sc, bv->bv_volid, MFI_MGMT_VD, bv); + goto done; + } + + strlcpy(bv->bv_dev, sc->sc_ld[i].ld_dev, sizeof(bv->bv_dev)); + + switch(sc->sc_ld_list.mll_list[i].mll_state) { + case MFI_LD_OFFLINE: + bv->bv_status = BIOC_SVOFFLINE; + break; + + case MFI_LD_PART_DEGRADED: + case MFI_LD_DEGRADED: + bv->bv_status = BIOC_SVDEGRADED; + break; + + case MFI_LD_ONLINE: + bv->bv_status = BIOC_SVONLINE; + break; + + default: + bv->bv_status = BIOC_SVINVALID; + DNPRINTF(MFI_D_IOCTL, "%s: invalid logical disk state %#x\n", + DEVNAME(sc), + sc->sc_ld_list.mll_list[i].mll_state); + } + + /* additional status can modify MFI status */ + switch (sc->sc_ld_details.mld_progress.mlp_in_prog) { + case MFI_LD_PROG_CC: + case MFI_LD_PROG_BGI: + bv->bv_status = BIOC_SVSCRUB; + per = (int)sc->sc_ld_details.mld_progress.mlp_cc.mp_progress; + bv->bv_percent = (per * 100) / 0xffff; + bv->bv_seconds = + sc->sc_ld_details.mld_progress.mlp_cc.mp_elapsed_seconds; + break; + + case MFI_LD_PROG_FGI: + case MFI_LD_PROG_RECONSTRUCT: + /* nothing yet */ + break; + } + + /* + * The RAID levels are determined per the SNIA DDF spec, this is only + * a subset that is valid for the MFI contrller. + */ + bv->bv_level = sc->sc_ld_details.mld_cfg.mlc_parm.mpa_pri_raid; + if (sc->sc_ld_details.mld_cfg.mlc_parm.mpa_sec_raid == + MFI_DDF_SRL_SPANNED) + bv->bv_level *= 10; + + bv->bv_nodisk = sc->sc_ld_details.mld_cfg.mlc_parm.mpa_no_drv_per_span * + sc->sc_ld_details.mld_cfg.mlc_parm.mpa_span_depth; + + bv->bv_size = sc->sc_ld_details.mld_size * 512; /* bytes per block */ + + rv = 0; +done: + return (rv); +} + +int +mfi_ioctl_disk(struct mfi_softc *sc, struct bioc_disk *bd) +{ + struct mfi_conf *cfg; + struct mfi_array *ar; + struct mfi_ld_cfg *ld; + struct mfi_pd_details *pd; + struct scsi_inquiry_data *inqbuf; + char vend[8+16+4+1]; + int i, rv = EINVAL; + int arr, vol, disk; + uint32_t size; + uint8_t mbox[MFI_MBOX_SIZE]; + + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_disk %#x\n", + DEVNAME(sc), bd->bd_diskid); + + pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK); + + /* send single element command to retrieve size for full structure */ + cfg = malloc(sizeof *cfg, M_DEVBUF, M_WAITOK); + if (mfi_mgmt(sc, MD_DCMD_CONF_GET, MFI_DATA_IN, sizeof *cfg, cfg, NULL)) + goto freeme; + + size = cfg->mfc_size; + free(cfg, M_DEVBUF); + + /* memory for read config */ + cfg = malloc(size, M_DEVBUF, M_WAITOK); + memset(cfg, 0, size); + if (mfi_mgmt(sc, MD_DCMD_CONF_GET, MFI_DATA_IN, size, cfg, NULL)) + goto freeme; + + ar = cfg->mfc_array; + + /* calculate offset to ld structure */ + ld = (struct mfi_ld_cfg *)( + ((uint8_t *)cfg) + offsetof(struct mfi_conf, mfc_array) + + cfg->mfc_array_size * cfg->mfc_no_array); + + vol = bd->bd_volid; + + if (vol >= cfg->mfc_no_ld) { + /* do hotspares */ + rv = mfi_bio_hs(sc, bd->bd_volid, MFI_MGMT_SD, bd); + goto freeme; + } + + /* find corresponding array for ld */ + for (i = 0, arr = 0; i < vol; i++) + arr += ld[i].mlc_parm.mpa_span_depth; + + /* offset disk into pd list */ + disk = bd->bd_diskid % ld[vol].mlc_parm.mpa_no_drv_per_span; + + /* offset array index into the next spans */ + arr += bd->bd_diskid / ld[vol].mlc_parm.mpa_no_drv_per_span; + + bd->bd_target = ar[arr].pd[disk].mar_enc_slot; + switch (ar[arr].pd[disk].mar_pd_state){ + case MFI_PD_UNCONFIG_GOOD: + bd->bd_status = BIOC_SDUNUSED; + break; + + case MFI_PD_HOTSPARE: /* XXX dedicated hotspare part of array? */ + bd->bd_status = BIOC_SDHOTSPARE; + break; + + case MFI_PD_OFFLINE: + bd->bd_status = BIOC_SDOFFLINE; + break; + + case MFI_PD_FAILED: + bd->bd_status = BIOC_SDFAILED; + break; + + case MFI_PD_REBUILD: + bd->bd_status = BIOC_SDREBUILD; + break; + + case MFI_PD_ONLINE: + bd->bd_status = BIOC_SDONLINE; + break; + + case MFI_PD_UNCONFIG_BAD: /* XXX define new state in bio */ + default: + bd->bd_status = BIOC_SDINVALID; + break; + + } + + /* get the remaining fields */ + *((uint16_t *)&mbox) = ar[arr].pd[disk].mar_pd.mfp_id; + if (mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN, + sizeof *pd, pd, mbox)) + goto freeme; + + bd->bd_size = pd->mpd_size * 512; /* bytes per block */ + + /* if pd->mpd_enc_idx is 0 then it is not in an enclosure */ + bd->bd_channel = pd->mpd_enc_idx; + + inqbuf = (struct scsi_inquiry_data *)&pd->mpd_inq_data; + memcpy(vend, inqbuf->vendor, sizeof vend - 1); + vend[sizeof vend - 1] = '\0'; + strlcpy(bd->bd_vendor, vend, sizeof(bd->bd_vendor)); + + /* XXX find a way to retrieve serial nr from drive */ + /* XXX find a way to get bd_procdev */ + + rv = 0; +freeme: + free(pd, M_DEVBUF); + free(cfg, M_DEVBUF); + + return (rv); +} + +int +mfi_ioctl_alarm(struct mfi_softc *sc, struct bioc_alarm *ba) +{ + uint32_t opc, dir = MFI_DATA_NONE; + int rv = 0; + int8_t ret; + + switch(ba->ba_opcode) { + case BIOC_SADISABLE: + opc = MR_DCMD_SPEAKER_DISABLE; + break; + + case BIOC_SAENABLE: + opc = MR_DCMD_SPEAKER_ENABLE; + break; + + case BIOC_SASILENCE: + opc = MR_DCMD_SPEAKER_SILENCE; + break; + + case BIOC_GASTATUS: + opc = MR_DCMD_SPEAKER_GET; + dir = MFI_DATA_IN; + break; + + case BIOC_SATEST: + opc = MR_DCMD_SPEAKER_TEST; + break; + + default: + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_alarm biocalarm invalid " + "opcode %x\n", DEVNAME(sc), ba->ba_opcode); + return (EINVAL); + } + + if (mfi_mgmt(sc, opc, dir, sizeof(ret), &ret, NULL)) + rv = EINVAL; + else + if (ba->ba_opcode == BIOC_GASTATUS) + ba->ba_status = ret; + else + ba->ba_status = 0; + + return (rv); +} + +int +mfi_ioctl_blink(struct mfi_softc *sc, struct bioc_blink *bb) +{ + int i, found, rv = EINVAL; + uint8_t mbox[MFI_MBOX_SIZE]; + uint32_t cmd; + struct mfi_pd_list *pd; + + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_blink %x\n", DEVNAME(sc), + bb->bb_status); + + /* channel 0 means not in an enclosure so can't be blinked */ + if (bb->bb_channel == 0) + return (EINVAL); + + pd = malloc(MFI_PD_LIST_SIZE, M_DEVBUF, M_WAITOK); + + if (mfi_mgmt(sc, MR_DCMD_PD_GET_LIST, MFI_DATA_IN, + MFI_PD_LIST_SIZE, pd, NULL)) + goto done; + + for (i = 0, found = 0; i < pd->mpl_no_pd; i++) + if (bb->bb_channel == pd->mpl_address[i].mpa_enc_index && + bb->bb_target == pd->mpl_address[i].mpa_enc_slot) { + found = 1; + break; + } + + if (!found) + goto done; + + memset(mbox, 0, sizeof mbox); + + *((uint16_t *)&mbox) = pd->mpl_address[i].mpa_pd_id;; + + switch (bb->bb_status) { + case BIOC_SBUNBLINK: + cmd = MR_DCMD_PD_UNBLINK; + break; + + case BIOC_SBBLINK: + cmd = MR_DCMD_PD_BLINK; + break; + + case BIOC_SBALARM: + default: + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_blink biocblink invalid " + "opcode %x\n", DEVNAME(sc), bb->bb_status); + goto done; + } + + + if (mfi_mgmt(sc, cmd, MFI_DATA_NONE, 0, NULL, mbox)) + goto done; + + rv = 0; +done: + free(pd, M_DEVBUF); + return (rv); +} + +int +mfi_ioctl_setstate(struct mfi_softc *sc, struct bioc_setstate *bs) +{ + struct mfi_pd_list *pd; + int i, found, rv = EINVAL; + uint8_t mbox[MFI_MBOX_SIZE]; + uint32_t cmd; + + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_setstate %x\n", DEVNAME(sc), + bs->bs_status); + + pd = malloc(MFI_PD_LIST_SIZE, M_DEVBUF, M_WAITOK); + + if (mfi_mgmt(sc, MR_DCMD_PD_GET_LIST, MFI_DATA_IN, + MFI_PD_LIST_SIZE, pd, NULL)) + goto done; + + for (i = 0, found = 0; i < pd->mpl_no_pd; i++) + if (bs->bs_channel == pd->mpl_address[i].mpa_enc_index && + bs->bs_target == pd->mpl_address[i].mpa_enc_slot) { + found = 1; + break; + } + + if (!found) + goto done; + + memset(mbox, 0, sizeof mbox); + + *((uint16_t *)&mbox) = pd->mpl_address[i].mpa_pd_id;; + + switch (bs->bs_status) { + case BIOC_SSONLINE: + mbox[2] = MFI_PD_ONLINE; + cmd = MD_DCMD_PD_SET_STATE; + break; + + case BIOC_SSOFFLINE: + mbox[2] = MFI_PD_OFFLINE; + cmd = MD_DCMD_PD_SET_STATE; + break; + + case BIOC_SSHOTSPARE: + mbox[2] = MFI_PD_HOTSPARE; + cmd = MD_DCMD_PD_SET_STATE; + break; +/* + case BIOC_SSREBUILD: + cmd = MD_DCMD_PD_REBUILD; + break; +*/ + default: + DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_setstate invalid " + "opcode %x\n", DEVNAME(sc), bs->bs_status); + goto done; + } + + + if (mfi_mgmt(sc, MD_DCMD_PD_SET_STATE, MFI_DATA_NONE, 0, NULL, mbox)) + goto done; + + rv = 0; +done: + free(pd, M_DEVBUF); + return (rv); +} + +int +mfi_bio_hs(struct mfi_softc *sc, int volid, int type, void *bio_hs) +{ + struct mfi_conf *cfg; + struct mfi_hotspare *hs; + struct mfi_pd_details *pd; + struct bioc_disk *sdhs; + struct bioc_vol *vdhs; + struct scsi_inquiry_data *inqbuf; + char vend[8+16+4+1]; + int i, rv = EINVAL; + uint32_t size; + uint8_t mbox[MFI_MBOX_SIZE]; + + DNPRINTF(MFI_D_IOCTL, "%s: mfi_vol_hs %d\n", DEVNAME(sc), volid); + + if (!bio_hs) + return (EINVAL); + + pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK); + + /* send single element command to retrieve size for full structure */ + cfg = malloc(sizeof *cfg, M_DEVBUF, M_WAITOK); + if (mfi_mgmt(sc, MD_DCMD_CONF_GET, MFI_DATA_IN, sizeof *cfg, cfg, NULL)) + goto freeme; + + size = cfg->mfc_size; + free(cfg, M_DEVBUF); + + /* memory for read config */ + cfg = malloc(size, M_DEVBUF, M_WAITOK); + memset(cfg, 0, size); + if (mfi_mgmt(sc, MD_DCMD_CONF_GET, MFI_DATA_IN, size, cfg, NULL)) + goto freeme; + + /* calculate offset to hs structure */ + hs = (struct mfi_hotspare *)( + ((uint8_t *)cfg) + offsetof(struct mfi_conf, mfc_array) + + cfg->mfc_array_size * cfg->mfc_no_array + + cfg->mfc_ld_size * cfg->mfc_no_ld); + + if (volid < cfg->mfc_no_ld) + goto freeme; /* not a hotspare */ + + if (volid > (cfg->mfc_no_ld + cfg->mfc_no_hs)) + goto freeme; /* not a hotspare */ + + /* offset into hotspare structure */ + i = volid - cfg->mfc_no_ld; + + DNPRINTF(MFI_D_IOCTL, "%s: mfi_vol_hs i %d volid %d no_ld %d no_hs %d " + "hs %p cfg %p id %02x\n", DEVNAME(sc), i, volid, cfg->mfc_no_ld, + cfg->mfc_no_hs, hs, cfg, hs[i].mhs_pd.mfp_id); + + /* get pd fields */ + memset(mbox, 0, sizeof mbox); + *((uint16_t *)&mbox) = hs[i].mhs_pd.mfp_id; + if (mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN, + sizeof *pd, pd, mbox)) { + DNPRINTF(MFI_D_IOCTL, "%s: mfi_vol_hs illegal PD\n", + DEVNAME(sc)); + goto freeme; + } + + switch (type) { + case MFI_MGMT_VD: + vdhs = bio_hs; + vdhs->bv_status = BIOC_SVONLINE; + vdhs->bv_size = pd->mpd_size / 2; /* XXX why? / 2 */ + vdhs->bv_level = -1; /* hotspare */ + vdhs->bv_nodisk = 1; + break; + + case MFI_MGMT_SD: + sdhs = bio_hs; + sdhs->bd_status = BIOC_SDHOTSPARE; + sdhs->bd_size = pd->mpd_size / 2; /* XXX why? / 2 */ + sdhs->bd_channel = pd->mpd_enc_idx; + sdhs->bd_target = pd->mpd_enc_slot; + inqbuf = (struct scsi_inquiry_data *)&pd->mpd_inq_data; + memcpy(vend, inqbuf->vendor, sizeof vend - 1); + vend[sizeof vend - 1] = '\0'; + strlcpy(sdhs->bd_vendor, vend, sizeof(sdhs->bd_vendor)); + break; + + default: + goto freeme; + } + + DNPRINTF(MFI_D_IOCTL, "%s: mfi_vol_hs 6\n", DEVNAME(sc)); + rv = 0; +freeme: + free(pd, M_DEVBUF); + free(cfg, M_DEVBUF); + + return (rv); +} + +int +mfi_create_sensors(struct mfi_softc *sc) +{ + struct device *dev; + struct scsibus_softc *ssc; + int i; + + TAILQ_FOREACH(dev, &alldevs, dv_list) { + if (dev->dv_parent != &sc->sc_dev) + continue; + + /* check if this is the scsibus for the logical disks */ + ssc = (struct scsibus_softc *)dev; + if (ssc->adapter_link == &sc->sc_link) + break; + } + + if (ssc == NULL) + return (1); + + sc->sc_sensors = malloc(sizeof(struct sensor) * sc->sc_ld_cnt, + M_DEVBUF, M_WAITOK); + if (sc->sc_sensors == NULL) + return (1); + bzero(sc->sc_sensors, sizeof(struct sensor) * sc->sc_ld_cnt); + + for (i = 0; i < sc->sc_ld_cnt; i++) { + if (ssc->sc_link[i][0] == NULL) + goto bad; + + dev = ssc->sc_link[i][0]->device_softc; + + sc->sc_sensors[i].type = SENSOR_DRIVE; + sc->sc_sensors[i].status = SENSOR_S_UNKNOWN; + + strlcpy(sc->sc_sensors[i].device, DEVNAME(sc), + sizeof(sc->sc_sensors[i].device)); + strlcpy(sc->sc_sensors[i].desc, dev->dv_xname, + sizeof(sc->sc_sensors[i].desc)); + + sensor_add(&sc->sc_sensors[i]); + } + + if (sensor_task_register(sc, mfi_refresh_sensors, 10) != 0) + goto bad; + + return (0); + +bad: + while (--i >= 0) + sensor_del(&sc->sc_sensors[i]); + free(sc->sc_sensors, M_DEVBUF); + + return (1); +} + +void +mfi_refresh_sensors(void *arg) +{ + struct mfi_softc *sc = arg; + int i; + struct bioc_vol bv; + + + for (i = 0; i < sc->sc_ld_cnt; i++) { + bzero(&bv, sizeof(bv)); + bv.bv_volid = i; + if (mfi_ioctl_vol(sc, &bv)) + return; + + switch(bv.bv_status) { + case BIOC_SVOFFLINE: + sc->sc_sensors[i].value = SENSOR_DRIVE_FAIL; + sc->sc_sensors[i].status = SENSOR_S_CRIT; + break; + + case BIOC_SVDEGRADED: + sc->sc_sensors[i].value = SENSOR_DRIVE_PFAIL; + sc->sc_sensors[i].status = SENSOR_S_WARN; + break; + + case BIOC_SVSCRUB: + case BIOC_SVONLINE: + sc->sc_sensors[i].value = SENSOR_DRIVE_ONLINE; + sc->sc_sensors[i].status = SENSOR_S_OK; + break; + + case BIOC_SVINVALID: + /* FALLTRHOUGH */ + default: + sc->sc_sensors[i].value = 0; /* unknown */ + sc->sc_sensors[i].status = SENSOR_S_UNKNOWN; + } + + } +} +#endif /* NBIO > 0 */ diff --git a/sys/dev/ic/mfireg.h b/sys/dev/ic/mfireg.h new file mode 100644 index 00000000000..d5654783263 --- /dev/null +++ b/sys/dev/ic/mfireg.h @@ -0,0 +1,928 @@ +/* $OpenBSD: mfireg.h,v 1.24 2006/06/19 19:05:45 marco Exp $ */ +/* + * Copyright (c) 2006 Marco Peereboom <marco@peereboom.us> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* management interface constants */ +#define MFI_MGMT_VD 0x01 +#define MFI_MGMT_SD 0x02 + +/* generic constants */ +#define MFI_FRAME_SIZE 64 +#define MFI_SENSE_SIZE 128 +#define MFI_OSTS_INTR_VALID 0x00000002 /* valid interrupt */ +#define MFI_INVALID_CTX 0xffffffff +#define MFI_ENABLE_INTR 0x01 +#define MFI_MAXFER MAXPHYS /* XXX bogus */ + +/* register offsets */ +#define MFI_IMSG0 0x10 /* inbound msg 0 */ +#define MFI_IMSG1 0x14 /* inbound msg 1 */ +#define MFI_OMSG0 0x18 /* outbound msg 0 */ +#define MFI_OMSG1 0x1c /* outbound msg 1 */ +#define MFI_IDB 0x20 /* inbound doorbell */ +#define MFI_ISTS 0x24 /* inbound intr stat */ +#define MFI_IMSK 0x28 /* inbound intr mask */ +#define MFI_ODB 0x2c /* outbound doorbell */ +#define MFI_OSTS 0x30 /* outbound intr stat */ +#define MFI_OMSK 0x34 /* outbound inter mask */ +#define MFI_IQP 0x40 /* inbound queue port */ +#define MFI_OQP 0x44 /* outbound queue port */ + +/* * firmware states */ +#define MFI_STATE_MASK 0xf0000000 +#define MFI_STATE_UNDEFINED 0x00000000 +#define MFI_STATE_BB_INIT 0x10000000 +#define MFI_STATE_FW_INIT 0x40000000 +#define MFI_STATE_WAIT_HANDSHAKE 0x60000000 +#define MFI_STATE_FW_INIT_2 0x70000000 +#define MFI_STATE_DEVICE_SCAN 0x80000000 +#define MFI_STATE_FLUSH_CACHE 0xa0000000 +#define MFI_STATE_READY 0xb0000000 +#define MFI_STATE_OPERATIONAL 0xc0000000 +#define MFI_STATE_FAULT 0xf0000000 +#define MFI_STATE_MAXSGL_MASK 0x00ff0000 +#define MFI_STATE_MAXCMD_MASK 0x0000ffff + +/* command reset register */ +#define MFI_INIT_ABORT 0x00000000 +#define MFI_INIT_READY 0x00000002 +#define MFI_INIT_MFIMODE 0x00000004 +#define MFI_INIT_CLEAR_HANDSHAKE 0x00000008 +#define MFI_RESET_FLAGS MFI_INIT_READY|MFI_INIT_MFIMODE + +/* mfi Frame flags */ +#define MFI_FRAME_POST_IN_REPLY_QUEUE 0x0000 +#define MFI_FRAME_DONT_POST_IN_REPLY_QUEUE 0x0001 +#define MFI_FRAME_SGL32 0x0000 +#define MFI_FRAME_SGL64 0x0002 +#define MFI_FRAME_SENSE32 0x0000 +#define MFI_FRAME_SENSE64 0x0004 +#define MFI_FRAME_DIR_NONE 0x0000 +#define MFI_FRAME_DIR_WRITE 0x0008 +#define MFI_FRAME_DIR_READ 0x0010 +#define MFI_FRAME_DIR_BOTH 0x0018 + +/* mfi command opcodes */ +#define MFI_CMD_INIT 0x00 +#define MFI_CMD_LD_READ 0x01 +#define MFI_CMD_LD_WRITE 0x02 +#define MFI_CMD_LD_SCSI_IO 0x03 +#define MFI_CMD_PD_SCSI_IO 0x04 +#define MFI_CMD_DCMD 0x05 +#define MFI_CMD_ABORT 0x06 +#define MFI_CMD_SMP 0x07 +#define MFI_CMD_STP 0x08 + +/* direct commands */ +#define MR_DCMD_CTRL_GET_INFO 0x01010000 +#define MR_DCMD_CTRL_CACHE_FLUSH 0x01101000 +#define MR_FLUSH_CTRL_CACHE 0x01 +#define MR_FLUSH_DISK_CACHE 0x02 +#define MR_DCMD_CTRL_SHUTDOWN 0x01050000 +#define MR_ENABLE_DRIVE_SPINDOWN 0x01 +#define MR_DCMD_CTRL_EVENT_GET_INFO 0x01040100 +#define MR_DCMD_CTRL_EVENT_GET 0x01040300 +#define MR_DCMD_CTRL_EVENT_WAIT 0x01040500 +#define MR_DCMD_PD_GET_LIST 0x02010000 +#define MR_DCMD_PD_GET_INFO 0x02020000 +#define MD_DCMD_PD_SET_STATE 0x02030100 +#define MD_DCMD_PD_REBUILD 0x02040100 +#define MR_DCMD_PD_BLINK 0x02070100 +#define MR_DCMD_PD_UNBLINK 0x02070200 +#define MR_DCMD_LD_GET_LIST 0x03010000 +#define MR_DCMD_LD_GET_INFO 0x03020000 +#define MR_DCMD_LD_GET_PROPERTIES 0x03030000 +#define MD_DCMD_CONF_GET 0x04010000 +#define MR_DCMD_CLUSTER 0x08000000 +#define MR_DCMD_CLUSTER_RESET_ALL 0x08010100 +#define MR_DCMD_CLUSTER_RESET_LD 0x08010200 + +#define MR_DCMD_SPEAKER_GET 0x01030100 +#define MR_DCMD_SPEAKER_ENABLE 0x01030200 +#define MR_DCMD_SPEAKER_DISABLE 0x01030300 +#define MR_DCMD_SPEAKER_SILENCE 0x01030400 +#define MR_DCMD_SPEAKER_TEST 0x01030500 + +/* mailbox bytes in direct command */ +#define MFI_MBOX_SIZE 12 + +/* mfi completion codes */ +typedef enum { + MFI_STAT_OK = 0x00, + MFI_STAT_INVALID_CMD = 0x01, + MFI_STAT_INVALID_DCMD = 0x02, + MFI_STAT_INVALID_PARAMETER = 0x03, + MFI_STAT_INVALID_SEQUENCE_NUMBER = 0x04, + MFI_STAT_ABORT_NOT_POSSIBLE = 0x05, + MFI_STAT_APP_HOST_CODE_NOT_FOUND = 0x06, + MFI_STAT_APP_IN_USE = 0x07, + MFI_STAT_APP_NOT_INITIALIZED = 0x08, + MFI_STAT_ARRAY_INDEX_INVALID = 0x09, + MFI_STAT_ARRAY_ROW_NOT_EMPTY = 0x0a, + MFI_STAT_CONFIG_RESOURCE_CONFLICT = 0x0b, + MFI_STAT_DEVICE_NOT_FOUND = 0x0c, + MFI_STAT_DRIVE_TOO_SMALL = 0x0d, + MFI_STAT_FLASH_ALLOC_FAIL = 0x0e, + MFI_STAT_FLASH_BUSY = 0x0f, + MFI_STAT_FLASH_ERROR = 0x10, + MFI_STAT_FLASH_IMAGE_BAD = 0x11, + MFI_STAT_FLASH_IMAGE_INCOMPLETE = 0x12, + MFI_STAT_FLASH_NOT_OPEN = 0x13, + MFI_STAT_FLASH_NOT_STARTED = 0x14, + MFI_STAT_FLUSH_FAILED = 0x15, + MFI_STAT_HOST_CODE_NOT_FOUNT = 0x16, + MFI_STAT_LD_CC_IN_PROGRESS = 0x17, + MFI_STAT_LD_INIT_IN_PROGRESS = 0x18, + MFI_STAT_LD_LBA_OUT_OF_RANGE = 0x19, + MFI_STAT_LD_MAX_CONFIGURED = 0x1a, + MFI_STAT_LD_NOT_OPTIMAL = 0x1b, + MFI_STAT_LD_RBLD_IN_PROGRESS = 0x1c, + MFI_STAT_LD_RECON_IN_PROGRESS = 0x1d, + MFI_STAT_LD_WRONG_RAID_LEVEL = 0x1e, + MFI_STAT_MAX_SPARES_EXCEEDED = 0x1f, + MFI_STAT_MEMORY_NOT_AVAILABLE = 0x20, + MFI_STAT_MFC_HW_ERROR = 0x21, + MFI_STAT_NO_HW_PRESENT = 0x22, + MFI_STAT_NOT_FOUND = 0x23, + MFI_STAT_NOT_IN_ENCL = 0x24, + MFI_STAT_PD_CLEAR_IN_PROGRESS = 0x25, + MFI_STAT_PD_TYPE_WRONG = 0x26, + MFI_STAT_PR_DISABLED = 0x27, + MFI_STAT_ROW_INDEX_INVALID = 0x28, + MFI_STAT_SAS_CONFIG_INVALID_ACTION = 0x29, + MFI_STAT_SAS_CONFIG_INVALID_DATA = 0x2a, + MFI_STAT_SAS_CONFIG_INVALID_PAGE = 0x2b, + MFI_STAT_SAS_CONFIG_INVALID_TYPE = 0x2c, + MFI_STAT_SCSI_DONE_WITH_ERROR = 0x2d, + MFI_STAT_SCSI_IO_FAILED = 0x2e, + MFI_STAT_SCSI_RESERVATION_CONFLICT = 0x2f, + MFI_STAT_SHUTDOWN_FAILED = 0x30, + MFI_STAT_TIME_NOT_SET = 0x31, + MFI_STAT_WRONG_STATE = 0x32, + MFI_STAT_LD_OFFLINE = 0x33, + MFI_STAT_PEER_NOTIFICATION_REJECTED = 0x34, + MFI_STAT_PEER_NOTIFICATION_FAILED = 0x35, + MFI_STAT_RESERVATION_IN_PROGRESS = 0x36, + MFI_STAT_I2C_ERRORS_DETECTED = 0x37, + MFI_STAT_PCI_ERRORS_DETECTED = 0x38, + MFI_STAT_INVALID_STATUS = 0xff +} mfi_status_t; + +typedef enum { + MFI_EVT_CLASS_DEBUG = -2, + MFI_EVT_CLASS_PROGRESS = -1, + MFI_EVT_CLASS_INFO = 0, + MFI_EVT_CLASS_WARNING = 1, + MFI_EVT_CLASS_CRITICAL = 2, + MFI_EVT_CLASS_FATAL = 3, + MFI_EVT_CLASS_DEAD = 4 +} mfi_evt_class_t; + +typedef enum { + MFI_EVT_LOCALE_LD = 0x0001, + MFI_EVT_LOCALE_PD = 0x0002, + MFI_EVT_LOCALE_ENCL = 0x0004, + MFI_EVT_LOCALE_BBU = 0x0008, + MFI_EVT_LOCALE_SAS = 0x0010, + MFI_EVT_LOCALE_CTRL = 0x0020, + MFI_EVT_LOCALE_CONFIG = 0x0040, + MFI_EVT_LOCALE_CLUSTER = 0x0080, + MFI_EVT_LOCALE_ALL = 0xffff +} mfi_evt_locale_t; + +typedef enum { + MR_EVT_ARGS_NONE = 0x00, + MR_EVT_ARGS_CDB_SENSE, + MR_EVT_ARGS_LD, + MR_EVT_ARGS_LD_COUNT, + MR_EVT_ARGS_LD_LBA, + MR_EVT_ARGS_LD_OWNER, + MR_EVT_ARGS_LD_LBA_PD_LBA, + MR_EVT_ARGS_LD_PROG, + MR_EVT_ARGS_LD_STATE, + MR_EVT_ARGS_LD_STRIP, + MR_EVT_ARGS_PD, + MR_EVT_ARGS_PD_ERR, + MR_EVT_ARGS_PD_LBA, + MR_EVT_ARGS_PD_LBA_LD, + MR_EVT_ARGS_PD_PROG, + MR_EVT_ARGS_PD_STATE, + MR_EVT_ARGS_PCI, + MR_EVT_ARGS_RATE, + MR_EVT_ARGS_STR, + MR_EVT_ARGS_TIME, + MR_EVT_ARGS_ECC +} mfi_evt_args; + +/* driver definitions */ +#define MFI_MAX_PD_CHANNELS 2 +#define MFI_MAX_PD_ARRAY 32 +#define MFI_MAX_LD_CHANNELS 2 +#define MFI_MAX_CHANNELS (MFI_MAX_PD_CHANNELS + MFI_MAX_LD_CHANNELS) +#define MFI_MAX_CHANNEL_DEVS 128 +#define MFI_DEFAULT_ID -1 +#define MFI_MAX_LUN 8 +#define MFI_MAX_LD 64 +#define MFI_MAX_SPAN 8 +#define MFI_MAX_ARRAY_DEDICATED 16 + +/* sense buffer */ +struct mfi_sense { + uint8_t mse_data[MFI_SENSE_SIZE]; +} __packed; + +/* scatter gather elements */ +struct mfi_sg32 { + uint32_t addr; + uint32_t len; +} __packed; + +struct mfi_sg64 { + uint64_t addr; + uint32_t len; +} __packed; + +union mfi_sgl { + struct mfi_sg32 sg32[1]; + struct mfi_sg64 sg64[1]; +} __packed; + +/* message frame */ +struct mfi_frame_header { + uint8_t mfh_cmd; + uint8_t mfh_sense_len; + uint8_t mfh_cmd_status; + uint8_t mfh_scsi_status; + uint8_t mfh_target_id; + uint8_t mfh_lun_id; + uint8_t mfh_cdb_len; + uint8_t mfh_sg_count; + uint32_t mfh_context; + uint32_t mfh_pad0; + uint16_t mfh_flags; + uint16_t mfh_timeout; + uint32_t mfh_data_len; +} __packed; + +union mfi_sgl_frame { + struct mfi_sg32 sge32[8]; + struct mfi_sg64 sge64[5]; + +} __packed; + +struct mfi_init_frame { + struct mfi_frame_header mif_header; + uint32_t mif_qinfo_new_addr_lo; + uint32_t mif_qinfo_new_addr_hi; + uint32_t mif_qinfo_old_addr_lo; + uint32_t mif_qinfo_old_addr_hi; + uint32_t mif_reserved[6]; +} __packed; + +/* queue init structure */ +struct mfi_init_qinfo { + uint32_t miq_flags; + uint32_t miq_rq_entries; + uint32_t miq_rq_addr_lo; + uint32_t miq_rq_addr_hi; + uint32_t miq_pi_addr_lo; + uint32_t miq_pi_addr_hi; + uint32_t miq_ci_addr_lo; + uint32_t miq_ci_addr_hi; +} __packed; + +#define MFI_IO_FRAME_SIZE 40 +struct mfi_io_frame { + struct mfi_frame_header mif_header; + uint32_t mif_sense_addr_lo; + uint32_t mif_sense_addr_hi; + uint32_t mif_lba_lo; + uint32_t mif_lba_hi; + union mfi_sgl mif_sgl; +} __packed; + +#define MFI_PASS_FRAME_SIZE 48 +struct mfi_pass_frame { + struct mfi_frame_header mpf_header; + uint32_t mpf_sense_addr_lo; + uint32_t mpf_sense_addr_hi; + uint8_t mpf_cdb[16]; + union mfi_sgl mpf_sgl; +} __packed; + +#define MFI_DCMD_FRAME_SIZE 40 +struct mfi_dcmd_frame { + struct mfi_frame_header mdf_header; + uint32_t mdf_opcode; + uint8_t mdf_mbox[MFI_MBOX_SIZE]; + union mfi_sgl mdf_sgl; +} __packed; + +struct mfi_abort_frame { + struct mfi_frame_header maf_header; + uint32_t maf_abort_context; + uint32_t maf_pad; + uint32_t maf_abort_mfi_addr_lo; + uint32_t maf_abort_mfi_addr_hi; + uint32_t maf_reserved[6]; +} __packed; + +struct mfi_smp_frame { + struct mfi_frame_header msf_header; + uint64_t msf_sas_addr; + union { + struct mfi_sg32 sg32[2]; + struct mfi_sg64 sg64[2]; + } msf_sgl; +} __packed; + +struct mfi_stp_frame { + struct mfi_frame_header msf_header; + uint16_t msf_fis[10]; + uint32_t msf_stp_flags; + union { + struct mfi_sg32 sg32[2]; + struct mfi_sg64 sg64[2]; + } msf_sgl; +} __packed; + +union mfi_frame { + struct mfi_frame_header mfr_header; + struct mfi_init_frame mfr_init; + struct mfi_io_frame mfr_io; + struct mfi_pass_frame mfr_pass; + struct mfi_dcmd_frame mfr_dcmd; + struct mfi_abort_frame mfr_abort; + struct mfi_smp_frame mfr_smp; + struct mfi_stp_frame mfr_stp; + uint8_t mfr_bytes[MFI_FRAME_SIZE]; +}; + +union mfi_evt_class_locale { + struct { + uint16_t locale; + uint8_t reserved; + int8_t class; + } __packed mec_members; + + uint32_t mec_word; +} __packed; + +struct mfi_evt_log_info { + uint32_t mel_newest_seq_num; + uint32_t mel_oldest_seq_num; + uint32_t mel_clear_seq_num; + uint32_t mel_shutdown_seq_num; + uint32_t mel_boot_seq_num; +} __packed; + +struct mfi_progress { + uint16_t mp_progress; + uint16_t mp_elapsed_seconds; +} __packed; + +struct mfi_evtarg_ld { + uint16_t mel_target_id; + uint8_t mel_ld_index; + uint8_t mel_reserved; +} __packed; + +struct mfi_evtarg_pd { + uint16_t mep_device_id; + uint8_t mep_encl_index; + uint8_t mep_slot_number; +} __packed; + +struct mfi_evt_detail { + uint32_t med_seq_num; + uint32_t med_time_stamp; + uint32_t med_code; + union mfi_evt_class_locale med_cl; + uint8_t med_arg_type; + uint8_t med_reserved1[15]; + + union { + struct { + struct mfi_evtarg_pd pd; + uint8_t cdb_length; + uint8_t sense_length; + uint8_t reserved[2]; + uint8_t cdb[16]; + uint8_t sense[64]; + } __packed cdb_sense; + + struct mfi_evtarg_ld ld; + + struct { + struct mfi_evtarg_ld ld; + uint64_t count; + } __packed ld_count; + + struct { + uint64_t lba; + struct mfi_evtarg_ld ld; + } __packed ld_lba; + + struct { + struct mfi_evtarg_ld ld; + uint32_t prev_owner; + uint32_t new_owner; + } __packed ld_owner; + + struct { + uint64_t ld_lba; + uint64_t pd_lba; + struct mfi_evtarg_ld ld; + struct mfi_evtarg_pd pd; + } __packed ld_lba_pd_lba; + + struct { + struct mfi_evtarg_ld ld; + struct mfi_progress prog; + } __packed ld_prog; + + struct { + struct mfi_evtarg_ld ld; + uint32_t prev_state; + uint32_t new_state; + } __packed ld_state; + + struct { + uint64_t strip; + struct mfi_evtarg_ld ld; + } __packed ld_strip; + + struct mfi_evtarg_pd pd; + + struct { + struct mfi_evtarg_pd pd; + uint32_t err; + } __packed pd_err; + + struct { + uint64_t lba; + struct mfi_evtarg_pd pd; + } __packed pd_lba; + + struct { + uint64_t lba; + struct mfi_evtarg_pd pd; + struct mfi_evtarg_ld ld; + } __packed pd_lba_ld; + + struct { + struct mfi_evtarg_pd pd; + struct mfi_progress prog; + } __packed pd_prog; + + struct { + struct mfi_evtarg_pd pd; + uint32_t prev_state; + uint32_t new_state; + } __packed pd_state; + + struct { + uint16_t vendor_id; + uint16_t device_id; + uint16_t subvendor_id; + uint16_t subdevice_id; + } __packed pci; + + uint32_t rate; + char str[96]; + + struct { + uint32_t rtc; + uint32_t elapsed_seconds; + } __packed time; + + struct { + uint32_t ecar; + uint32_t elog; + char str[64]; + } __packed ecc; + + uint8_t b[96]; + uint16_t s[48]; + uint32_t w[24]; + uint64_t d[12]; + } args; + + char med_description[128]; +} __packed; + +/* controller properties from mfi_ctrl_info */ +struct mfi_ctrl_props { + uint16_t mcp_seq_num; + uint16_t mcp_pred_fail_poll_interval; + uint16_t mcp_intr_throttle_cnt; + uint16_t mcp_intr_throttle_timeout; + uint8_t mcp_rebuild_rate; + uint8_t mcp_patrol_read_rate; + uint8_t mcp_bgi_rate; + uint8_t mcp_cc_rate; + uint8_t mcp_recon_rate; + uint8_t mcp_cache_flush_interval; + uint8_t mcp_spinup_drv_cnt; + uint8_t mcp_spinup_delay; + uint8_t mcp_cluster_enable; + uint8_t mcp_coercion_mode; + uint8_t mcp_alarm_enable; + uint8_t mcp_disable_auto_rebuild; + uint8_t mcp_disable_battery_warn; + uint8_t mcp_ecc_bucket_size; + uint16_t mcp_ecc_bucket_leak_rate; + uint8_t mcp_restore_hotspare_on_insertion; + uint8_t mcp_expose_encl_devices; + uint8_t mcp_reserved[38]; +} __packed; + +/* pci info */ +struct mfi_info_pci { + uint16_t mip_vendor; + uint16_t mip_device; + uint16_t mip_subvendor; + uint16_t mip_subdevice; + uint8_t mip_reserved[24]; +} __packed; + +/* host interface infor */ +struct mfi_info_host { + uint8_t mih_type; +#define MFI_INFO_HOST_PCIX 0x01 +#define MFI_INFO_HOST_PCIE 0x02 +#define MFI_INFO_HOST_ISCSI 0x04 +#define MFI_INFO_HOST_SAS3G 0x08 + uint8_t mih_reserved[6]; + uint8_t mih_port_count; + uint64_t mih_port_addr[8]; +} __packed; + +/* device interface info */ +struct mfi_info_device { + uint8_t mid_type; +#define MFI_INFO_DEV_SPI 0x01 +#define MFI_INFO_DEV_SAS3G 0x02 +#define MFI_INFO_DEV_SATA1 0x04 +#define MFI_INFO_DEV_SATA3G 0x08 + uint8_t mid_reserved[6]; + uint8_t mid_port_count; + uint64_t mid_port_addr[8]; +} __packed; + +/* firmware component info */ +struct mfi_info_component { + char mic_name[8]; + char mic_version[32]; + char mic_build_date[16]; + char mic_build_time[16]; +} __packed; + +/* controller info from MFI_DCMD_CTRL_GETINFO. */ +struct mfi_ctrl_info { + struct mfi_info_pci mci_pci; + struct mfi_info_host mci_host; + struct mfi_info_device mci_device; + + /* Firmware components that are present and active. */ + uint32_t mci_image_check_word; + uint32_t mci_image_component_count; + struct mfi_info_component mci_image_component[8]; + + /* Firmware components that have been flashed but are inactive */ + uint32_t mci_pending_image_component_count; + struct mfi_info_component mci_pending_image_component[8]; + + uint8_t mci_max_arms; + uint8_t mci_max_spans; + uint8_t mci_max_arrays; + uint8_t mci_max_lds; + char mci_product_name[80]; + char mci_serial_number[32]; + uint32_t mci_hw_present; +#define MFI_INFO_HW_BBU 0x01 +#define MFI_INFO_HW_ALARM 0x02 +#define MFI_INFO_HW_NVRAM 0x04 +#define MFI_INFO_HW_UART 0x08 + uint32_t mci_current_fw_time; + uint16_t mci_max_cmds; + uint16_t mci_max_sg_elements; + uint32_t mci_max_request_size; + uint16_t mci_lds_present; + uint16_t mci_lds_degraded; + uint16_t mci_lds_offline; + uint16_t mci_pd_present; + uint16_t mci_pd_disks_present; + uint16_t mci_pd_disks_pred_failure; + uint16_t mci_pd_disks_failed; + uint16_t mci_nvram_size; + uint16_t mci_memory_size; + uint16_t mci_flash_size; + uint16_t mci_ram_correctable_errors; + uint16_t mci_ram_uncorrectable_errors; + uint8_t mci_cluster_allowed; + uint8_t mci_cluster_active; + uint16_t mci_max_strips_per_io; + + uint32_t mci_raid_levels; +#define MFI_INFO_RAID_0 0x01 +#define MFI_INFO_RAID_1 0x02 +#define MFI_INFO_RAID_5 0x04 +#define MFI_INFO_RAID_1E 0x08 +#define MFI_INFO_RAID_6 0x10 + + uint32_t mci_adapter_ops; +#define MFI_INFO_AOPS_RBLD_RATE 0x0001 +#define MFI_INFO_AOPS_CC_RATE 0x0002 +#define MFI_INFO_AOPS_BGI_RATE 0x0004 +#define MFI_INFO_AOPS_RECON_RATE 0x0008 +#define MFI_INFO_AOPS_PATROL_RATE 0x0010 +#define MFI_INFO_AOPS_ALARM_CONTROL 0x0020 +#define MFI_INFO_AOPS_CLUSTER_SUPPORTED 0x0040 +#define MFI_INFO_AOPS_BBU 0x0080 +#define MFI_INFO_AOPS_SPANNING_ALLOWED 0x0100 +#define MFI_INFO_AOPS_DEDICATED_SPARES 0x0200 +#define MFI_INFO_AOPS_REVERTIBLE_SPARES 0x0400 +#define MFI_INFO_AOPS_FOREIGN_IMPORT 0x0800 +#define MFI_INFO_AOPS_SELF_DIAGNOSTIC 0x1000 +#define MFI_INFO_AOPS_MIXED_ARRAY 0x2000 +#define MFI_INFO_AOPS_GLOBAL_SPARES 0x4000 + + uint32_t mci_ld_ops; +#define MFI_INFO_LDOPS_READ_POLICY 0x01 +#define MFI_INFO_LDOPS_WRITE_POLICY 0x02 +#define MFI_INFO_LDOPS_IO_POLICY 0x04 +#define MFI_INFO_LDOPS_ACCESS_POLICY 0x08 +#define MFI_INFO_LDOPS_DISK_CACHE_POLICY 0x10 + + struct { + uint8_t min; + uint8_t max; + uint8_t reserved[2]; + } __packed mci_stripe_sz_ops; + + uint32_t mci_pd_ops; +#define MFI_INFO_PDOPS_FORCE_ONLINE 0x01 +#define MFI_INFO_PDOPS_FORCE_OFFLINE 0x02 +#define MFI_INFO_PDOPS_FORCE_REBUILD 0x04 + + uint32_t mci_pd_mix_support; +#define MFI_INFO_PDMIX_SAS 0x01 +#define MFI_INFO_PDMIX_SATA 0x02 +#define MFI_INFO_PDMIX_ENCL 0x04 +#define MFI_INFO_PDMIX_LD 0x08 +#define MFI_INFO_PDMIX_SATA_CLUSTER 0x10 + + uint8_t mci_ecc_bucket_count; + uint8_t mci_reserved2[11]; + struct mfi_ctrl_props mci_properties; + char mci_package_version[0x60]; + uint8_t mci_pad[0x800 - 0x6a0]; +} __packed; + +/* logical disk info from MR_DCMD_LD_GET_LIST */ +struct mfi_ld { + uint8_t mld_target; + uint8_t mld_res; + uint16_t mld_seq; +} __packed; + +struct mfi_ld_list { + uint32_t mll_no_ld; + uint32_t mll_res; + struct { + struct mfi_ld mll_ld; + uint8_t mll_state; +#define MFI_LD_OFFLINE 0x00 +#define MFI_LD_PART_DEGRADED 0x01 +#define MFI_LD_DEGRADED 0x02 +#define MFI_LD_ONLINE 0x03 + uint8_t mll_res2; + uint8_t mll_res3; + uint8_t mll_res4; + u_quad_t mll_size; + } mll_list[MFI_MAX_LD]; +} __packed; + +/* logicl disk details from MR_DCMD_LD_GET_INFO */ +struct mfi_ld_prop { + struct mfi_ld mlp_ld; + char mlp_name[16]; + uint8_t mlp_cache_policy; + uint8_t mlp_acces_policy; + uint8_t mlp_diskcache_policy; + uint8_t mlp_cur_cache_policy; + uint8_t mlp_disable_bgi; + uint8_t mlp_res[7]; +} __packed; + +struct mfi_ld_parm { + uint8_t mpa_pri_raid; /* SNIA DDF PRL */ +#define MFI_DDF_PRL_RAID0 0x00 +#define MFI_DDF_PRL_RAID1 0x01 +#define MFI_DDF_PRL_RAID3 0x03 +#define MFI_DDF_PRL_RAID4 0x04 +#define MFI_DDF_PRL_RAID5 0x05 +#define MFI_DDF_PRL_RAID1E 0x11 +#define MFI_DDF_PRL_JBOD 0x0f +#define MFI_DDF_PRL_CONCAT 0x1f +#define MFI_DDF_PRL_RAID5E 0x15 +#define MFI_DDF_PRL_RAID5EE 0x25 +#define MFI_DDF_PRL_RAID6 0x16 + uint8_t mpa_raid_qual; /* SNIA DDF RLQ */ + uint8_t mpa_sec_raid; /* SNIA DDF SRL */ +#define MFI_DDF_SRL_STRIPED 0x00 +#define MFI_DDF_SRL_MIRRORED 0x01 +#define MFI_DDF_SRL_CONCAT 0x02 +#define MFI_DDF_SRL_SPANNED 0x03 + uint8_t mpa_stripe_size; + uint8_t mpa_no_drv_per_span; + uint8_t mpa_span_depth; + uint8_t mpa_state; + uint8_t mpa_init_state; + uint8_t mpa_res[24]; +} __packed; + +struct mfi_ld_span { + u_quad_t mls_start_block; + u_quad_t mls_no_blocks; + uint16_t mls_index; + uint8_t mls_res[6]; +} __packed; + +struct mfi_ld_cfg { + struct mfi_ld_prop mlc_prop; + struct mfi_ld_parm mlc_parm; + struct mfi_ld_span mlc_span[MFI_MAX_SPAN]; +} __packed; + +struct mfi_ld_progress { + uint32_t mlp_in_prog; +#define MFI_LD_PROG_CC 0x01 +#define MFI_LD_PROG_BGI 0x02 +#define MFI_LD_PROG_FGI 0x04 +#define MFI_LD_PROG_RECONSTRUCT 0x08 + struct mfi_progress mlp_cc; + struct mfi_progress mlp_bgi; + struct mfi_progress mlp_fgi; + struct mfi_progress mlp_reconstruct; + struct mfi_progress mlp_res[4]; +} __packed; + +struct mfi_ld_details { + struct mfi_ld_cfg mld_cfg; + u_quad_t mld_size; + struct mfi_ld_progress mld_progress; + uint16_t mld_clust_own_id; + uint8_t mld_res1; + uint8_t mld_res2; + uint8_t mld_inq_page83[64]; + uint8_t mld_res[16]; +} __packed; + +/* physical disk info from MR_DCMD_PD_GET_LIST */ +struct mfi_pd_address { + uint16_t mpa_pd_id; + uint16_t mpa_enc_id; + uint8_t mpa_enc_index; + uint8_t mpa_enc_slot; + uint8_t mpa_scsi_type; + uint8_t mpa_port; + u_quad_t mpa_sas_address[2]; +} __packed; + +struct mfi_pd_list { + uint32_t mpl_size; + uint32_t mpl_no_pd; + struct mfi_pd_address mpl_address[1]; +} __packed; +#define MFI_PD_LIST_SIZE (256 * sizeof(struct mfi_pd_address) + 8) + +struct mfi_pd { + uint16_t mfp_id; + uint16_t mfp_seq; +} __packed; + +struct mfi_pd_progress { + uint32_t mfp_in_prog; +#define MFI_PD_PROG_RBLD 0x01 +#define MFI_PD_PROG_PR 0x02 +#define MFI_PD_PROG_CLEAR 0x04 + struct mfi_progress mfp_rebuild; + struct mfi_progress mfp_patrol_read; + struct mfi_progress mfp_clear; + struct mfi_progress mfp_res[4]; +} __packed; + +struct mfi_pd_details { + struct mfi_pd mpd_pd; + uint8_t mpd_inq_data[96]; + uint8_t mpd_inq_page83[64]; + uint8_t mpd_no_support; + uint8_t mpd_scsy_type; + uint8_t mpd_port; + uint8_t mpd_speed; + uint32_t mpd_mediaerr_cnt; + uint32_t mpd_othererr_cnt; + uint32_t mpd_predfail_cnt; + uint32_t mpd_last_pred_event; + uint16_t mpd_fw_state; + uint8_t mpd_rdy_for_remove; + uint8_t mpd_link_speed; + uint32_t mpd_ddf_state; +#define MFI_DDF_GUID_FORCED 0x01 +#define MFI_DDF_PART_OF_VD 0x02 +#define MFI_DDF_GLOB_HOTSPARE 0x04 +#define MFI_DDF_HOTSPARE 0x08 +#define MFI_DDF_FOREIGN 0x10 +#define MFI_DDF_TYPE_MASK 0xf000 +#define MFI_DDF_TYPE_UNKNOWN 0x0000 +#define MFI_DDF_TYPE_PAR_SCSI 0x1000 +#define MFI_DDF_TYPE_SAS 0x2000 +#define MFI_DDF_TYPE_SATA 0x3000 +#define MFI_DDF_TYPE_FC 0x4000 + struct { + uint8_t mpp_cnt; + uint8_t mpp_severed; + uint8_t mpp_res[6]; + u_quad_t mpp_sas_addr[4]; + } __packed mpd_path; + u_quad_t mpd_size; + u_quad_t mpd_no_coerce_size; + u_quad_t mpd_coerce_size; + uint16_t mpd_enc_id; + uint8_t mpd_enc_idx; + uint8_t mpd_enc_slot; + struct mfi_pd_progress mpd_progress; + uint8_t mpd_bblock_full; + uint8_t mpd_unusable; + uint8_t mpd_res[218]; /* size is 512 */ +} __packed; + +/* array configuration from MD_DCMD_CONF_GET */ +struct mfi_array { + u_quad_t mar_smallest_pd; + uint8_t mar_no_disk; + uint8_t mar_res1; + uint16_t mar_array_ref; + uint8_t mar_res2[20]; + struct { + struct mfi_pd mar_pd; + uint16_t mar_pd_state; +#define MFI_PD_UNCONFIG_GOOD 0x00 +#define MFI_PD_UNCONFIG_BAD 0x01 +#define MFI_PD_HOTSPARE 0x02 +#define MFI_PD_OFFLINE 0x10 +#define MFI_PD_FAILED 0x11 +#define MFI_PD_REBUILD 0x14 +#define MFI_PD_ONLINE 0x18 + uint8_t mar_enc_pd; + uint8_t mar_enc_slot; + } pd[MFI_MAX_PD_ARRAY]; +} __packed; + +struct mfi_hotspare { + struct mfi_pd mhs_pd; + uint8_t mhs_type; +#define MFI_PD_HS_DEDICATED 0x01 +#define MFI_PD_HS_REVERTIBLE 0x02 +#define MFI_PD_HS_ENC_AFFINITY 0x04 + uint8_t mhs_res[2]; + uint8_t mhs_array_max; + uint16_t mhs_array_ref[MFI_MAX_ARRAY_DEDICATED]; +} __packed; + +struct mfi_conf { + uint32_t mfc_size; + uint16_t mfc_no_array; + uint16_t mfc_array_size; + uint16_t mfc_no_ld; + uint16_t mfc_ld_size; + uint16_t mfc_no_hs; + uint16_t mfc_hs_size; + uint8_t mfc_res[16]; + /* + * XXX this is a ridiculous hack and does not reflect reality + * Structures are actually indexed and therefore need pointer + * math to reach. We need the size of this structure first so + * call it with the size of this structure and then use the returned + * values to allocate memory and do the transfer of the whole structure + * then calculate pointers to each of these structures. + */ + struct mfi_array mfc_array[1]; + struct mfi_ld_cfg mfc_ld[1]; + struct mfi_hotspare mfc_hs[1]; +} __packed; diff --git a/sys/dev/ic/mfivar.h b/sys/dev/ic/mfivar.h new file mode 100644 index 00000000000..46e2ae6f55d --- /dev/null +++ b/sys/dev/ic/mfivar.h @@ -0,0 +1,148 @@ +/* $OpenBSD: mfivar.h,v 1.28 2006/08/31 18:18:46 marco Exp $ */ +/* + * Copyright (c) 2006 Marco Peereboom <marco@peereboom.us> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define DEVNAME(_s) ((_s)->sc_dev.dv_xname) + +/* #define MFI_DEBUG */ +#ifdef MFI_DEBUG +extern uint32_t mfi_debug; +#define DPRINTF(x...) do { if (mfi_debug) printf(x); } while(0) +#define DNPRINTF(n,x...) do { if (mfi_debug & n) printf(x); } while(0) +#define MFI_D_CMD 0x0001 +#define MFI_D_INTR 0x0002 +#define MFI_D_MISC 0x0004 +#define MFI_D_DMA 0x0008 +#define MFI_D_IOCTL 0x0010 +#define MFI_D_RW 0x0020 +#define MFI_D_MEM 0x0040 +#define MFI_D_CCB 0x0080 +#else +#define DPRINTF(x...) +#define DNPRINTF(n,x...) +#endif + +struct mfi_mem { + bus_dmamap_t am_map; + bus_dma_segment_t am_seg; + size_t am_size; + caddr_t am_kva; +}; + +#define MFIMEM_MAP(_am) ((_am)->am_map) +#define MFIMEM_DVA(_am) ((_am)->am_map->dm_segs[0].ds_addr) +#define MFIMEM_KVA(_am) ((void *)(_am)->am_kva) + +struct mfi_prod_cons { + uint32_t mpc_producer; + uint32_t mpc_consumer; + uint32_t mpc_reply_q[1]; /* compensate for 1 extra reply per spec */ +}; + +struct mfi_ccb { + struct mfi_softc *ccb_sc; + + union mfi_frame *ccb_frame; + paddr_t ccb_pframe; + uint32_t ccb_frame_size; + uint32_t ccb_extra_frames; + + struct mfi_sense *ccb_sense; + paddr_t ccb_psense; + + bus_dmamap_t ccb_dmamap; + + union mfi_sgl *ccb_sgl; + + /* data for sgl */ + void *ccb_data; + uint32_t ccb_len; + + uint32_t ccb_direction; +#define MFI_DATA_NONE 0 +#define MFI_DATA_IN 1 +#define MFI_DATA_OUT 2 + + struct scsipi_xfer *ccb_xs; + + void (*ccb_done)(struct mfi_ccb *); + + volatile enum { + MFI_CCB_FREE, + MFI_CCB_READY, + MFI_CCB_DONE + } ccb_state; + uint32_t ccb_flags; +#define MFI_CCB_F_ERR (1<<0) + TAILQ_ENTRY(mfi_ccb) ccb_link; +}; + +TAILQ_HEAD(mfi_ccb_list, mfi_ccb); + +struct mfi_softc { + struct device sc_dev; + struct scsipi_channel sc_chan; + struct scsipi_adapter sc_adapt; + + void *sc_ih; + + u_int32_t sc_flags; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_dma_tag_t sc_dmat; + + /* save some useful information for logical drives that is missing + * in sc_ld_list + */ + struct { + uint32_t ld_present; + char ld_dev[16]; /* device name sd? */ + } sc_ld[MFI_MAX_LD]; + + /* scsi ioctl from sd device */ + int (*sc_ioctl)(struct device *, u_long, caddr_t); + + /* firmware determined max, totals and other information*/ + uint32_t sc_max_cmds; + uint32_t sc_max_sgl; + uint32_t sc_max_ld; + uint32_t sc_ld_cnt; + /* XXX these struct should be local to mgmt function */ + struct mfi_ctrl_info sc_info; + struct mfi_ld_list sc_ld_list; + struct mfi_ld_details sc_ld_details; + + /* all commands */ + struct mfi_ccb *sc_ccb; + + /* producer/consumer pointers and reply queue */ + struct mfi_mem *sc_pcq; + + /* frame memory */ + struct mfi_mem *sc_frames; + uint32_t sc_frames_size; + + /* sense memory */ + struct mfi_mem *sc_sense; + + struct mfi_ccb_list sc_ccb_freeq; + + struct sensor *sc_sensors; +}; + +int mfi_attach(struct mfi_softc *sc); +int mfi_intr(void *); diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index dba1a99f80f..ce41d0439bd 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.273 2006/11/30 21:01:16 bouyer Exp $ +# $NetBSD: files.pci,v 1.274 2006/12/17 22:57:16 bouyer Exp $ # # Config file and device description for machine-independent PCI code. # Included by ports that need it. Requires that the SCSI files be @@ -132,6 +132,11 @@ file dev/pci/bha_pci.c bha_pci attach isp at pci with isp_pci file dev/pci/isp_pci.c isp_pci +# LSILogic MegaRAID SAS +# device declaration in sys/conf/files +attach mfi at pci with mfi_pci +file dev/pci/mfi_pci.c mfi_pci + # LSILogic Fusion-MPT I/O Processor family # device declaration in sys/conf/files attach mpt at pci with mpt_pci diff --git a/sys/dev/pci/mfi_pci.c b/sys/dev/pci/mfi_pci.c new file mode 100644 index 00000000000..fba1c82bc1d --- /dev/null +++ b/sys/dev/pci/mfi_pci.c @@ -0,0 +1,156 @@ +/* $OpenBSD: mfi_pci.c,v 1.11 2006/08/06 04:40:08 brad Exp $ */ +/* + * Copyright (c) 2006 Marco Peereboom <marco@peereboom.us> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcivar.h> + +#include <machine/bus.h> + +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsipi_disk.h> +#include <dev/scsipi/scsiconf.h> + +#include <dev/ic/mfireg.h> +#include <dev/ic/mfivar.h> + +#define MFI_BAR 0x10 +#define MFI_PCI_MEMSIZE 0x2000 /* 8k */ + +int mfi_pci_find_device(void *); +int mfi_pci_match(struct device *, struct cfdata *, void *); +void mfi_pci_attach(struct device *, struct device *, void *); + +CFATTACH_DECL(mfi_pci, sizeof(struct mfi_softc), + mfi_pci_match, mfi_pci_attach, NULL, NULL); + +struct mfi_pci_device { + pcireg_t mpd_vendor; + pcireg_t mpd_product; + pcireg_t mpd_subvendor; + pcireg_t mpd_subproduct; + const char *mpd_model; + uint32_t mpd_flags; +}; + +static const +struct mfi_pci_device mfi_pci_devices[] = { + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_MEGARAID_SAS, + 0, 0, "", 0 }, + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_MEGARAID_VERDE_ZCR, + 0, 0, "", 0 }, + { PCI_VENDOR_DELL, PCI_PRODUCT_DELL_PERC_5, + PCI_VENDOR_DELL, 0x1f01, "Dell PERC 5/e", 0 }, + { PCI_VENDOR_DELL, PCI_PRODUCT_DELL_PERC_5, + PCI_VENDOR_DELL, 0x1f02, "Dell PERC 5/i", 0 }, + { 0, 0, + 0, 0, NULL, 0} +}; + +int +mfi_pci_find_device(void *aux) { + struct pci_attach_args *pa = aux; + int i; + + for (i = 0; mfi_pci_devices[i].mpd_vendor; i++) { + if (mfi_pci_devices[i].mpd_vendor == PCI_VENDOR(pa->pa_id) && + mfi_pci_devices[i].mpd_product == PCI_PRODUCT(pa->pa_id)) { + DNPRINTF(MFI_D_MISC, "mfi_pci_find_device: %i\n", i); + return (i); + } + } + + return (-1); +} + +int +mfi_pci_match(struct device *parent, struct cfdata *match, void *aux) +{ + int i; + + if ((i = mfi_pci_find_device(aux)) != -1) { + DNPRINTF(MFI_D_MISC, + "mfi_pci_match: vendor: %04x product: %04x\n", + mfi_pci_devices[i].mpd_vendor, + mfi_pci_devices[i].mpd_product); + + return (1); + } + + return (0); +} + +void +mfi_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct mfi_softc *sc = (struct mfi_softc *)self; + struct pci_attach_args *pa = aux; + const char *intrstr; + pci_intr_handle_t ih; + bus_size_t size; + pcireg_t csr; + uint32_t subsysid, i; + + subsysid = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); + for (i = 0; mfi_pci_devices[i].mpd_vendor; i++) + if (mfi_pci_devices[i].mpd_subvendor == PCI_VENDOR(subsysid) && + mfi_pci_devices[i].mpd_subproduct == PCI_PRODUCT(subsysid)){ + aprint_normal(", %s", + mfi_pci_devices[i].mpd_model); + break; + } + + csr = pci_mapreg_type(pa->pa_pc, pa->pa_tag, MFI_BAR); + csr |= PCI_MAPREG_MEM_TYPE_32BIT; + if (pci_mapreg_map(pa, MFI_BAR, csr, 0, + &sc->sc_iot, &sc->sc_ioh, NULL, &size)) { + aprint_error(": can't map controller pci space\n"); + return; + } + + sc->sc_dmat = pa->pa_dmat; + + if (pci_intr_map(pa, &ih)) { + aprint_error(": can't map interrupt\n"); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, size); + return; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO, mfi_intr, sc); + if (!sc->sc_ih) { + aprint_error(": can't establish interrupt"); + if (intrstr) + aprint_error(" at %s", intrstr); + aprint_error("\n"); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, size); + return; + } + + aprint_normal(": %s\n", intrstr); + + if (mfi_attach(sc)) { + aprint_error("%s: can't attach", DEVNAME(sc)); + pci_intr_disestablish(pa->pa_pc, sc->sc_ih); + sc->sc_ih = NULL; + bus_space_unmap(sc->sc_iot, sc->sc_ioh, size); + } +} |
