/* $OpenBSD: arc.c,v 1.127 2024/09/04 07:54:52 mglocker Exp $ */ /* * Copyright (c) 2006 David Gwynne * * 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. */ /* * Ching Huang Support ARC1880,1882,1213,1223,1214 */ #include "bio.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if NBIO > 0 #include #include #endif #ifdef ARC_DEBUG #define ARC_D_INIT (1<<0) #define ARC_D_RW (1<<1) #define ARC_D_DB (1<<2) int arcdebug = 0; #define DPRINTF(p...) do { if (arcdebug) printf(p); } while (0) #define DNPRINTF(n, p...) do { if ((n) & arcdebug) printf(p); } while (0) #else #define DPRINTF(p...) /* p */ #define DNPRINTF(n, p...) /* n, p */ #endif #define ARC_HBA_TYPE_A 0x00000001 #define ARC_HBA_TYPE_B 0x00000002 #define ARC_HBA_TYPE_C 0x00000003 #define ARC_HBA_TYPE_D 0x00000004 #define ARC_RA_PCI_BAR PCI_MAPREG_START #define ARC_RB_DOORBELL_BAR PCI_MAPREG_START #define ARC_RB_RWBUFFER_BAR PCI_MAPREG_PPB_END #define ARC_RC_PCI_BAR PCI_MAPREG_PCB_END #define ARC_RD_PCI_BAR PCI_MAPREG_START #define ARCMSR_MAX_CCB_COUNT 264 #define ARCMSR_MAX_HBB_POSTQUEUE 264 #define ARCMSR_MAX_HBD_POSTQUEUE 256 /* Areca boards using the Intel IOP are Type A (RA) */ #define ARC_RA_INB_MSG0 0x0010 #define ARC_RA_INB_MSG0_NOP (0x00000000) #define ARC_RA_INB_MSG0_GET_CONFIG (0x00000001) #define ARC_RA_INB_MSG0_SET_CONFIG (0x00000002) #define ARC_RA_INB_MSG0_ABORT_CMD (0x00000003) #define ARC_RA_INB_MSG0_STOP_BGRB (0x00000004) #define ARC_RA_INB_MSG0_FLUSH_CACHE (0x00000005) #define ARC_RA_INB_MSG0_START_BGRB (0x00000006) #define ARC_RA_INB_MSG0_CHK331PENDING (0x00000007) #define ARC_RA_INB_MSG0_SYNC_TIMER (0x00000008) #define ARC_RA_INB_MSG1 0x0014 #define ARC_RA_OUTB_ADDR0 0x0018 #define ARC_RA_OUTB_ADDR1 0x001c #define ARC_RA_OUTB_ADDR1_FIRMWARE_OK (1<<31) #define ARC_RA_INB_DOORBELL 0x0020 #define ARC_RA_INB_DOORBELL_WRITE_OK (1<<0) #define ARC_RA_INB_DOORBELL_READ_OK (1<<1) #define ARC_RA_OUTB_DOORBELL 0x002c #define ARC_RA_OUTB_DOORBELL_WRITE_OK (1<<0) #define ARC_RA_OUTB_DOORBELL_READ_OK (1<<1) #define ARC_RA_INTRSTAT 0x0030 #define ARC_RA_INTRSTAT_MSG0 (1<<0) #define ARC_RA_INTRSTAT_MSG1 (1<<1) #define ARC_RA_INTRSTAT_DOORBELL (1<<2) #define ARC_RA_INTRSTAT_POSTQUEUE (1<<3) #define ARC_RA_INTRSTAT_PCI (1<<4) #define ARC_RA_INTR_STAT_ALL 0x1F #define ARC_RA_INTRMASK 0x0034 #define ARC_RA_INTRMASK_MSG0 (1<<0) #define ARC_RA_INTRMASK_MSG1 (1<<1) #define ARC_RA_INTRMASK_DOORBELL (1<<2) #define ARC_RA_INTRMASK_POSTQUEUE (1<<3) #define ARC_RA_INTRMASK_PCI (1<<4) #define ARC_RA_INTR_MASK_ALL 0x1F #define ARC_RA_POST_QUEUE 0x0040 #define ARC_RA_POST_QUEUE_ADDR_SHIFT 5 #define ARC_RA_POST_QUEUE_IAMBIOS (1<<30) #define ARC_RA_POST_QUEUE_BIGFRAME (1<<31) #define ARC_RA_REPLY_QUEUE 0x0044 #define ARC_RA_REPLY_QUEUE_ADDR_SHIFT 5 #define ARC_RA_REPLY_QUEUE_ERR (1<<28) #define ARC_RA_REPLY_QUEUE_IAMBIOS (1<<30) #define ARC_RA_MSGBUF 0x0a00 #define ARC_RA_MSGBUF_LEN 1024 #define ARC_RA_IOC_WBUF_LEN 0x0e00 #define ARC_RA_IOC_WBUF 0x0e04 #define ARC_RA_IOC_RBUF_LEN 0x0f00 #define ARC_RA_IOC_RBUF 0x0f04 #define ARC_RA_IOC_RWBUF_MAXLEN 124 /* for both RBUF and WBUF */ /* Areca boards using the Marvel IOP0 are Type B (RB) */ #define ARC_RB_DRV2IOP_DOORBELL 0x00020400 #define ARC_RB_DRV2IOP_DOORBELL_MASK 0x00020404 #define ARC_RB_IOP2DRV_DOORBELL 0x00020408 #define ARC_RB_IOP2DRV_DOORBELL_FIRMWARE_OK (1<<31) #define ARC_RB_IOP2DRV_DOORBELL_MASK 0x0002040c /* Areca boards using the LSI IOP are Type C (RC) */ #define ARC_RC_INB_DOORBELL 0x20 #define ARC_RC_INTR_STAT 0x30 #define ARC_RC_INTR_MASK 0x34 #define ARC_RC_OUTB_DOORBELL 0x9C #define ARC_RC_OUTB_DOORBELL_CLR 0xA0 #define ARC_RC_D2I_MSG_CMD_DONE 0x08 #define ARC_RC_I2D_MSG_CMD_DONE 0x08 #define ARC_RC_I2D_MSG_CMD_DONE_CLR 0x08 #define ARC_RC_INB_MSGADDR0 0xB0 #define ARC_RC_INB_MSGADDR1 0xB4 #define ARC_RC_OUTB_MSGADDR0 0xB8 #define ARC_RC_OUTB_MSGADDR1 0xBC #define ARC_RC_OUTB_MSG_FIRMWARE_OK 0x80000000 #define ARC_RC_INB_POSTQ_LOW 0xC0 #define ARC_RC_INB_POSTQ_HIGH 0xC4 #define ARC_RC_OUTB_REPLYQ_LOW 0xC8 #define ARC_RC_OUTB_REPLYQ_HIGH 0xCC #define ARC_RC_MSG_WBUF_LEN 0x2000 #define ARC_RC_MSG_WBUF 0x2004 #define ARC_RC_MSG_RBUF_LEN 0x2100 #define ARC_RC_MSG_RBUF 0x2104 #define ARC_RC_MSG_RWBUF 0x2200 #define ARC_RC_INB_MSG0_NOP (0x00000000) #define ARC_RC_INB_MSG0_GET_CONFIG (0x00000001) #define ARC_RC_INB_MSG0_SET_CONFIG (0x00000002) #define ARC_RC_INB_MSG0_ABORT_CMD (0x00000003) #define ARC_RC_INB_MSG0_STOP_BGRB (0x00000004) #define ARC_RC_INB_MSG0_FLUSH_CACHE (0x00000005) #define ARC_RC_INB_MSG0_START_BGRB (0x00000006) #define ARC_RC_INB_MSG0_CHK331PENDING (0x00000007) #define ARC_RC_INB_MSG0_SYNC_TIMER (0x00000008) #define ARC_RC_D2I_DATA_WRITE_OK 0x00000002 #define ARC_RC_D2I_DATA_READ_OK 0x00000004 #define ARC_RC_D2I_MESSAGE_CMD_DONE 0x00000008 #define ARC_RC_D2I_POSTQUEUE_THROTTLING 0x00000010 #define ARC_RC_I2D_DATA_WRITE_OK 0x00000002 #define ARC_RC_I2D_DATA_WRITE_OK_CLEAR 0x00000002 #define ARC_RC_I2D_DATA_READ_OK 0x00000004 #define ARC_RC_I2D_DATA_READ_OK_CLEAR 0x00000004 #define ARC_RC_I2D_MESSAGE_CMD_DONE 0x00000008 #define ARC_RC_I2D_MESSAGE_CMD_DONE_CLEAR 0x00000008 #define ARC_RC_MESSAGE_FIRMWARE_OK 0x80000000 #define ARC_RC_INTR_STAT_UTILITY_A (1<<0) #define ARC_RC_INTR_STAT_DOORBELL (1<<2) #define ARC_RC_INTR_STAT_POSTQUEUE (1<<3) #define ARC_RC_INTR_MASK_ALL 0x0000000D #define ARC_RC_INTR_MASK_UTILITY_A (1<<0) #define ARC_RC_INTR_MASK_DOORBELL (1<<2) #define ARC_RC_INTR_MASK_POSTQUEUE (1<<3) #define ARC_RC_REPLY_QUEUE_ERR 1 #define ARC_RC_THROTTLE 12 /* Areca boards using the Marvell IOP 9580 are Type D (RD) */ #define ARC_RD_INTR_STAT 0x200 #define ARC_RD_HOST_INT_ENABLE 0x204 #define ARC_RD_INTR_ENABLE 0x20C #define ARC_RD_D2I_MSG_CMD_DONE 0x08 #define ARC_RD_I2D_MSG_CMD_DONE 0x2000000 #define ARC_RD_I2D_MSG_CMD_DONE_CLR 0x2000000 #define ARC_RD_INB_MSGADDR0 0x400 #define ARC_RD_INB_MSGADDR1 0x404 #define ARC_RD_OUTB_MSGADDR0 0x420 #define ARC_RD_OUTB_MSGADDR1 0x424 #define ARC_RD_INB_DOORBELL 0x460 #define ARC_RD_OUTB_DOORBELL 0x480 #define ARC_RD_OUTB_DOORBELL_CLR 0x480 #define ARC_RD_OUTB_DOORBELL_ENABLE 0x484 #define ARC_RD_OUTB_MSG_FIRMWARE_OK 0x80000000 #define ARC_RD_INB_POSTQ_LOW 0x1000 #define ARC_RD_INB_POSTQ_HIGH 0x1004 #define ARC_RD_OUTB_REPLYQ_LOW 0x1060 #define ARC_RD_OUTB_REPLYQ_HIGH 0x1064 #define ARC_RD_INB_WRITE_PTR 0x1018 #define ARC_RD_INB_READ_PTR 0x101C #define ARC_RD_OUTB_COPY_PTR 0x106C #define ARC_RD_OUTB_READ_PTR 0x1070 #define ARC_RD_OUTB_INTR_CAUSE 0x1088 #define ARC_RD_OUTB_INT_ENABLE 0x108C #define ARC_RD_MSG_WBUF_LEN 0x2000 #define ARC_RD_MSG_WBUF 0x2004 #define ARC_RD_MSG_RBUF_LEN 0x2100 #define ARC_RD_MSG_RBUF 0x2104 #define ARC_RD_MSG_RWBUF 0x2200 #define ARC_RD_INB_MSG0_NOP (0x00000000) #define ARC_RD_INB_MSG0_GET_CONFIG (0x00000001) #define ARC_RD_INB_MSG0_SET_CONFIG (0x00000002) #define ARC_RD_INB_MSG0_ABORT_CMD (0x00000003) #define ARC_RD_INB_MSG0_STOP_BGRB (0x00000004) #define ARC_RD_INB_MSG0_FLUSH_CACHE (0x00000005) #define ARC_RD_INB_MSG0_START_BGRB (0x00000006) #define ARC_RD_INB_MSG0_CHK331PENDING (0x00000007) #define ARC_RD_INB_MSG0_SYNC_TIMER (0x00000008) #define ARC_RD_D2I_DATA_WRITE_OK 0x00000001 #define ARC_RD_D2I_DATA_READ_OK 0x00000002 #define ARC_RD_D2I_MESSAGE_CMD_DONE 0x02000000 #define ARC_RD_D2I_POSTQUEUE_THROTTLING 0x00000010 #define ARC_RD_I2D_DATA_WRITE_OK 0x00000001 #define ARC_RD_I2D_DATA_WRITE_CLEAR 0x00000001 #define ARC_RD_I2D_DATA_READ_OK 0x00000002 #define ARC_RD_I2D_DATA_READ_CLEAR 0x00000002 #define ARC_RD_I2D_MESSAGE_CMD_DONE 0x02000000 #define ARC_RD_I2D_MESSAGE_CMD_DONE_CLEAR 0x02000000 #define ARC_RD_MESSAGE_FIRMWARE_OK 0x80000000 #define ARC_RD_INTR_STAT_DOORBELL 0x00001000 #define ARC_RD_INTR_STAT_POSTQUEUE 0x00000010 #define ARC_RD_INTR_ENABLE_ALL 0x00001010 #define ARC_RD_INTR_DISABLE_ALL 0x00000000 #define ARC_RD_INTR_ENABLE_DOORBELL 0x00001000 #define ARC_RD_INTR_ENABLE_POSTQUEUE 0x00000010 #define ARC_RD_REPLY_QUEUE_ERR 1 #define ARC_RD_OUTB_LIST_INT_CLR 1 struct arc_msg_firmware_info { u_int32_t signature; #define ARC_FWINFO_SIGNATURE_GET_CONFIG (0x87974060) #define ARC_FWINFO_SIGNATURE_SET_CONFIG (0x87974063) u_int32_t request_len; u_int32_t queue_len; u_int32_t sdram_size; u_int32_t sata_ports; u_int8_t vendor[40]; u_int8_t model[8]; u_int8_t fw_version[16]; u_int8_t device_map[16]; u_int32_t cfgVersion; u_int8_t cfgSerial[16]; u_int32_t cfgPicStatus; } __packed; /* definitions of the firmware commands sent via the doorbells */ struct arc_fw_hdr { u_int8_t byte1; u_int8_t byte2; u_int8_t byte3; } __packed; /* the fw header must always equal this */ struct arc_fw_hdr arc_fw_hdr = { 0x5e, 0x01, 0x61 }; struct arc_fw_bufhdr { struct arc_fw_hdr hdr; u_int16_t len; } __packed; #define ARC_FW_RAIDINFO 0x20 /* opcode + raid# */ #define ARC_FW_VOLINFO 0x21 /* opcode + vol# */ #define ARC_FW_DISKINFO 0x22 /* opcode + physdisk# */ #define ARC_FW_SYSINFO 0x23 /* opcode. reply is fw_sysinfo */ #define ARC_FW_MUTE_ALARM 0x30 /* opcode only */ #define ARC_FW_SET_ALARM 0x31 /* opcode + 1 byte for setting */ #define ARC_FW_SET_ALARM_DISABLE 0x00 #define ARC_FW_SET_ALARM_ENABLE 0x01 #define ARC_FW_NOP 0x38 /* opcode only */ #define ARC_FW_CMD_OK 0x41 #define ARC_FW_BLINK 0x43 #define ARC_FW_BLINK_ENABLE 0x00 #define ARC_FW_BLINK_DISABLE 0x01 #define ARC_FW_CMD_PASS_REQD 0x4d struct arc_fw_comminfo { u_int8_t baud_rate; u_int8_t data_bits; u_int8_t stop_bits; u_int8_t parity; u_int8_t flow_control; } __packed; struct arc_fw_scsiattr { u_int8_t channel; /* channel for SCSI target (0/1) */ u_int8_t target; u_int8_t lun; u_int8_t tagged; u_int8_t cache; u_int8_t speed; } __packed; struct arc_fw_raidinfo { u_int8_t set_name[16]; u_int32_t capacity; u_int32_t capacity2; u_int32_t fail_mask; u_int8_t device_array[32]; u_int8_t member_devices; u_int8_t new_member_devices; u_int8_t raid_state; u_int8_t volumes; u_int8_t volume_list[16]; u_int8_t reserved1[3]; u_int8_t free_segments; u_int32_t raw_stripes[8]; u_int32_t reserved2[3]; u_int8_t vol_ListX[112]; u_int8_t devEncArray[32]; } __packed; struct arc_fw_volinfo { u_int8_t set_name[16]; u_int32_t capacity; u_int32_t capacity2; u_int32_t fail_mask; u_int32_t stripe_size; /* in blocks */ u_int32_t new_fail_mask; u_int32_t new_stripe_size; u_int32_t volume_status; #define ARC_FW_VOL_STATUS_NORMAL 0x00 #define ARC_FW_VOL_STATUS_INITTING (1<<0) #define ARC_FW_VOL_STATUS_FAILED (1<<1) #define ARC_FW_VOL_STATUS_MIGRATING (1<<2) #define ARC_FW_VOL_STATUS_REBUILDING (1<<3) #define ARC_FW_VOL_STATUS_NEED_INIT (1<<4) #define ARC_FW_VOL_STATUS_NEED_MIGRATE (1<<5) #define ARC_FW_VOL_STATUS_INIT_FLAG (1<<6) #define ARC_FW_VOL_STATUS_NEED_REGEN (1<<7) #define ARC_FW_VOL_STATUS_CHECKING (1<<8) #define ARC_FW_VOL_STATUS_NEED_CHECK (1<<9) u_int32_t progress; struct arc_fw_scsiattr scsi_attr; u_int8_t member_disks; u_int8_t raid_level; #define ARC_FW_VOL_RAIDLEVEL_0 0x00 #define ARC_FW_VOL_RAIDLEVEL_1 0x01 #define ARC_FW_VOL_RAIDLEVEL_3 0x02 #define ARC_FW_VOL_RAIDLEVEL_5 0x03 #define ARC_FW_VOL_RAIDLEVEL_6 0x04 #define ARC_FW_VOL_RAIDLEVEL_PASSTHRU 0x05 u_int8_t new_member_disks; u_int8_t new_raid_level; u_int8_t raid_set_number; u_int8_t vol_state0; u_int32_t host_speed; u_int32_t vol_state; u_int8_t vol_array[16]; u_int8_t num_5060volumes; u_int8_t reserved[43]; } __packed; struct arc_fw_diskinfo { u_int8_t model[40]; u_int8_t serial[20]; u_int8_t firmware_rev[8]; u_int32_t capacity; u_int32_t capacity2; u_int8_t device_state; u_int8_t pio_mode; u_int8_t current_udma_mode; u_int8_t udma_mode; u_int8_t hot_spare_type; u_int8_t raid_number; /* 0xff unowned */ struct arc_fw_scsiattr scsi_attr; u_int8_t reserved[170]; } __packed; struct arc_fw_sysinfo { u_int8_t vendor_name[40]; u_int8_t serial_number[16]; u_int8_t firmware_version[16]; u_int8_t boot_version[16]; u_int8_t mb_version[16]; u_int8_t model_name[8]; u_int8_t local_ip[4]; u_int8_t current_ip[4]; u_int32_t time_tick; u_int32_t cpu_speed; u_int32_t icache; u_int32_t dcache; u_int32_t scache; u_int32_t memory_size; u_int32_t memory_speed; u_int32_t events; u_int8_t gsiMacAddress[6]; u_int8_t gsiDhcp; u_int8_t alarm; u_int8_t channel_usage; u_int8_t max_ata_mode; u_int8_t sdram_ecc; u_int8_t rebuild_priority; struct arc_fw_comminfo comm_a; struct arc_fw_comminfo comm_b; u_int8_t ide_channels; u_int8_t scsi_host_channels; u_int8_t ide_host_channels; u_int8_t max_volume_set; u_int8_t max_raid_set; u_int8_t ether_port; u_int8_t raid6_engine; u_int8_t reserved[75]; } __packed; struct arc_iop; struct arc_ccb; SLIST_HEAD(arc_ccb_list, arc_ccb); struct InBound_SRB { u_int32_t addressLow; /* pointer to SRB block */ u_int32_t addressHigh; u_int32_t length; /* in DWORDs */ u_int32_t reserved0; }; struct OutBound_SRB { u_int32_t addressLow; /* pointer to SRB block */ u_int32_t addressHigh; }; struct arc_HBD_Msgu { struct InBound_SRB post_qbuffer[ARCMSR_MAX_HBD_POSTQUEUE]; struct OutBound_SRB done_qbuffer[ARCMSR_MAX_HBD_POSTQUEUE+1]; u_int16_t postq_index; u_int16_t doneq_index; }; #define ARC_MAX_CMDQ_PTR_LEN sizeof(struct arc_HBD_Msgu) struct arc_msg_scsicmd { u_int8_t bus; u_int8_t target; u_int8_t lun; u_int8_t function; u_int8_t cdb_len; u_int8_t sgl_len; u_int8_t flags; #define ARC_MSG_SCSICMD_FLAG_SGL_BSIZE_512 (1<<0) #define ARC_MSG_SCSICMD_FLAG_FROM_BIOS (1<<1) #define ARC_MSG_SCSICMD_FLAG_WRITE (1<<2) #define ARC_MSG_SCSICMD_FLAG_SIMPLEQ (0x00) #define ARC_MSG_SCSICMD_FLAG_HEADQ (0x08) #define ARC_MSG_SCSICMD_FLAG_ORDERQ (0x10) u_int8_t msgPages; u_int32_t context; u_int32_t data_len; #define ARC_MSG_CDBLEN 16 u_int8_t cdb[ARC_MSG_CDBLEN]; u_int8_t status; #define ARC_MSG_STATUS_SELTIMEOUT 0xf0 #define ARC_MSG_STATUS_ABORTED 0xf1 #define ARC_MSG_STATUS_INIT_FAIL 0xf2 #define ARC_MSG_SENSELEN 15 u_int8_t sense_data[ARC_MSG_SENSELEN]; /* followed by an sgl */ } __packed; struct arc_sge { u_int32_t sg_length; #define ARC_SGE_64BIT (1<<24) u_int32_t sg_lo_addr; u_int32_t sg_hi_addr; } __packed; #define ARC_MAX_TARGET 16 #define ARC_MAX_LUN 8 #define ARC_MAX_IOCMDLEN 512 #define ARC_BLOCKSIZE 512 /* the firmware deals with up to 256 or 512 byte command frames. */ /* sizeof(struct arc_msg_scsicmd) + (sizeof(struct arc_sge) * 38) == 508 */ #define ARC_SGL_MAXLEN 38 /* sizeof(struct arc_msg_scsicmd) + (sizeof(struct arc_sge) * 17) == 252 */ #define ARC_SGL_256LEN 17 struct arc_io_cmd { struct arc_msg_scsicmd cmd; struct arc_sge sgl[ARC_SGL_MAXLEN]; u_int32_t reserved1; struct arc_ccb *ccb; u_int32_t reserved2[6]; } __packed; #define ARC_IO_CMD_LEN 512+32 /* stuff to manage a scsi command */ struct arc_ccb { struct arc_softc *ccb_sc; struct scsi_xfer *ccb_xs; bus_dmamap_t ccb_dmamap; bus_addr_t cmd_dma_offset; struct arc_io_cmd *ccb_cmd; u_int32_t ccb_cmd_post; SLIST_ENTRY(arc_ccb) ccb_link; u_int32_t arc_io_cmd_length; }; struct arc_softc { struct device sc_dev; const struct arc_iop *sc_iop; pci_chipset_tag_t sc_pc; pcitag_t sc_tag; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; bus_size_t sc_ios; bus_dma_tag_t sc_dmat; void *sc_ih; u_int32_t sc_req_count; struct arc_dmamem *sc_requests; struct arc_ccb *sc_ccbs; struct arc_ccb_list sc_ccb_free; struct mutex sc_ccb_mtx; struct scsi_iopool sc_iopool; struct scsibus_softc *sc_scsibus; struct rwlock sc_lock; volatile int sc_talking; struct ksensor *sc_sensors; struct ksensordev sc_sensordev; int sc_nsensors; u_int32_t sc_ledmask; u_int32_t sc_adp_type; u_int32_t sc_ccb_phys_hi; u_int32_t postQ_buffer; u_int32_t doneQ_buffer; bus_addr_t cmdQ_ptr_offset; struct arc_HBD_Msgu *pmu; }; #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) /* wrap up the bus_dma api */ struct arc_dmamem { bus_dmamap_t adm_map; bus_dma_segment_t adm_seg; size_t adm_size; caddr_t adm_kva; }; #define ARC_DMA_MAP(_adm) ((_adm)->adm_map) #define ARC_DMA_DVA(_adm) ((_adm)->adm_map->dm_segs[0].ds_addr) #define ARC_DMA_KVA(_adm) ((void *)(_adm)->adm_kva) int arc_match(struct device *, void *, void *); void arc_attach(struct device *, struct device *, void *); int arc_detach(struct device *, int); int arc_activate(struct device *, int); int arc_intr(void *); int arc_intr_A(void *); int arc_intr_C(void *); int arc_intr_D(void *); /* interface for scsi midlayer to talk to */ void arc_scsi_cmd(struct scsi_xfer *); /* code to deal with getting bits in and out of the bus space */ u_int32_t arc_read(struct arc_softc *, bus_size_t); void arc_read_region(struct arc_softc *, bus_size_t, void *, size_t); void arc_write(struct arc_softc *, bus_size_t, u_int32_t); void arc_write_region(struct arc_softc *, bus_size_t, void *, size_t); int arc_wait_eq(struct arc_softc *, bus_size_t, u_int32_t, u_int32_t); int arc_wait_ne(struct arc_softc *, bus_size_t, u_int32_t, u_int32_t); int arc_msg0(struct arc_softc *, u_int32_t); struct arc_dmamem *arc_dmamem_alloc(struct arc_softc *, size_t); void arc_dmamem_free(struct arc_softc *, struct arc_dmamem *); void arc_free_ccb_src(struct arc_softc *sc); int arc_alloc_ccbs(struct arc_softc *); struct arc_ccb *arc_get_ccb(struct arc_softc *); void arc_put_ccb(struct arc_softc *, struct arc_ccb *); int arc_load_xs(struct arc_ccb *); int arc_complete(struct arc_softc *, struct arc_ccb *, int); void arc_scsi_cmd_done(struct arc_softc *, struct arc_ccb *, u_int32_t); int arc_map_pci_resources(struct arc_softc *, struct pci_attach_args *); void arc_unmap_pci_resources(struct arc_softc *); int arc_chipA_firmware(struct arc_softc *); int arc_chipB_firmware(struct arc_softc *); int arc_chipC_firmware(struct arc_softc *); int arc_chipD_firmware(struct arc_softc *); void arc_enable_all_intr(struct arc_softc *); void arc_disable_all_intr(struct arc_softc *); void arc_stop_bgrb_proc(struct arc_softc *sc); void arc_flush_cache(struct arc_softc *sc); void arc_iop_set_conf(struct arc_softc *sc); #if NBIO > 0 /* stuff to do messaging via the doorbells */ void arc_lock(struct arc_softc *); void arc_unlock(struct arc_softc *); void arc_wait(struct arc_softc *); u_int8_t arc_msg_cksum(void *, u_int16_t); int arc_msgbuf(struct arc_softc *, void *, size_t, void *, size_t, int); /* bioctl */ int arc_bioctl(struct device *, u_long, caddr_t); int arc_bio_inq(struct arc_softc *, struct bioc_inq *); int arc_bio_vol(struct arc_softc *, struct bioc_vol *); int arc_bio_disk(struct arc_softc *, struct bioc_disk *); int arc_bio_alarm(struct arc_softc *, struct bioc_alarm *); int arc_bio_alarm_state(struct arc_softc *, struct bioc_alarm *); int arc_bio_blink(struct arc_softc *, struct bioc_blink *); int arc_bio_getvol(struct arc_softc *, int, struct arc_fw_volinfo *); #ifndef SMALL_KERNEL struct arc_task { struct task t; struct arc_softc *sc; }; /* sensors */ void arc_create_sensors(void *); void arc_refresh_sensors(void *); #endif /* SMALL_KERNEL */ #endif const struct cfattach arc_ca = { sizeof(struct arc_softc), arc_match, arc_attach, arc_detach, arc_activate }; struct cfdriver arc_cd = { NULL, "arc", DV_DULL }; const struct scsi_adapter arc_switch = { arc_scsi_cmd, NULL, NULL, NULL, NULL }; /* real stuff for dealing with the hardware */ struct arc_iop { int (*iop_query_firmware)(struct arc_softc *); }; static const struct arc_iop arc_intel = { arc_chipA_firmware }; static const struct arc_iop arc_marvell = { arc_chipB_firmware }; static const struct arc_iop arc_lsi = { arc_chipC_firmware }; static const struct arc_iop arc_marvell2 = { arc_chipD_firmware }; struct arc_board { pcireg_t ab_vendor; pcireg_t ab_product; const struct arc_iop *ab_iop; }; const struct arc_board *arc_match_board(struct pci_attach_args *); static const struct arc_board arc_devices[] = { { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1110, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1120, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1130, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1160, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1170, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1200, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1200_B, &arc_marvell }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1202, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1210, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1214, &arc_marvell2 }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1220, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1230, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1260, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1270, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1280, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1380, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1381, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1680, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1681, &arc_intel }, { PCI_VENDOR_ARECA, PCI_PRODUCT_ARECA_ARC1880, &arc_lsi } }; const struct arc_board * arc_match_board(struct pci_attach_args *pa) { const struct arc_board *ab; int i; for (i = 0; i < sizeof(arc_devices) / sizeof(arc_devices[0]); i++) { ab = &arc_devices[i]; if (PCI_VENDOR(pa->pa_id) == ab->ab_vendor && PCI_PRODUCT(pa->pa_id) == ab->ab_product) return (ab); } return (NULL); } int arc_match(struct device *parent, void *match, void *aux) { return ((arc_match_board(aux) == NULL) ? 0 : 1); } void arc_attach(struct device *parent, struct device *self, void *aux) { struct arc_softc *sc = (struct arc_softc *)self; struct pci_attach_args *pa = aux; struct scsibus_attach_args saa; sc->sc_talking = 0; rw_init(&sc->sc_lock, "arcmsg"); sc->sc_iop = arc_match_board(pa)->ab_iop; if(sc->sc_iop == &arc_intel) sc->sc_adp_type = ARC_HBA_TYPE_A; else if(sc->sc_iop == &arc_marvell) sc->sc_adp_type = ARC_HBA_TYPE_B; else if(sc->sc_iop == &arc_lsi) sc->sc_adp_type = ARC_HBA_TYPE_C; else if(sc->sc_iop == &arc_marvell2) sc->sc_adp_type = ARC_HBA_TYPE_D; if (arc_map_pci_resources(sc, pa) != 0) { /* error message printed by arc_map_pci_resources */ return; } if (arc_alloc_ccbs(sc) != 0) { /* error message printed by arc_alloc_ccbs */ goto unmap_pci; } arc_iop_set_conf(sc); if (sc->sc_iop->iop_query_firmware(sc) != 0) { /* error message printed by arc_query_firmware */ goto unmap_pci; } saa.saa_adapter = &arc_switch; saa.saa_adapter_softc = sc; saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET; saa.saa_adapter_buswidth = ARC_MAX_TARGET; saa.saa_luns = 8; saa.saa_openings = sc->sc_req_count; saa.saa_pool = &sc->sc_iopool; saa.saa_quirks = saa.saa_flags = 0; saa.saa_wwpn = saa.saa_wwnn = 0; sc->sc_scsibus = (struct scsibus_softc *)config_found(self, &saa, scsiprint); /* enable interrupts */ arc_enable_all_intr(sc); #if NBIO > 0 if (bio_register(self, arc_bioctl) != 0) panic("%s: bioctl registration failed", DEVNAME(sc)); #ifndef SMALL_KERNEL /* * you need to talk to the firmware to get volume info. our firmware * interface relies on being able to sleep, so we need to use a thread * to do the work. */ { struct arc_task *at; at = malloc(sizeof(*at), M_TEMP, M_WAITOK); at->sc = sc; task_set(&at->t, arc_create_sensors, at); task_add(systq, &at->t); } #endif #endif return; unmap_pci: arc_unmap_pci_resources(sc); } int arc_activate(struct device *self, int act) { int rv = 0; switch (act) { case DVACT_POWERDOWN: rv = config_activate_children(self, act); arc_detach(self, 0); break; default: rv = config_activate_children(self, act); break; } return (rv); } int arc_detach(struct device *self, int flags) { struct arc_softc *sc = (struct arc_softc *)self; arc_stop_bgrb_proc(sc); arc_flush_cache(sc); return (0); } int arc_intr_A(void *arg) { struct arc_softc *sc = arg; struct arc_ccb *ccb = NULL; char *kva = ARC_DMA_KVA(sc->sc_requests); struct arc_io_cmd *cmd; u_int32_t reg, intrstat, error; int ret = 0; intrstat = arc_read(sc, ARC_RA_INTRSTAT); intrstat &= ARC_RA_INTRSTAT_POSTQUEUE | ARC_RA_INTRSTAT_DOORBELL | ARC_RA_INTRSTAT_MSG0; arc_write(sc, ARC_RA_INTRSTAT, intrstat); if (intrstat & ARC_RA_INTRSTAT_DOORBELL) { ret = 1; if (sc->sc_talking) { /* if an ioctl is talking, wake it up */ arc_write(sc, ARC_RA_INTRMASK, ~ARC_RA_INTRMASK_POSTQUEUE); wakeup(sc); } else { /* otherwise drop it */ reg = arc_read(sc, ARC_RA_OUTB_DOORBELL); arc_write(sc, ARC_RA_OUTB_DOORBELL, reg); if (reg & ARC_RA_OUTB_DOORBELL_WRITE_OK) arc_write(sc, ARC_RA_INB_DOORBELL, ARC_RA_INB_DOORBELL_READ_OK); } } if (intrstat & ARC_RA_INTRSTAT_POSTQUEUE) { while ((reg = arc_read(sc, ARC_RA_REPLY_QUEUE)) != 0xffffffff) { ret = 1; cmd = (struct arc_io_cmd *)(kva + ((reg << ARC_RA_REPLY_QUEUE_ADDR_SHIFT) - (u_int32_t)ARC_DMA_DVA(sc->sc_requests))); ccb = cmd->ccb; bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests), ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); error = (reg & ARC_RA_REPLY_QUEUE_ERR)? 1:0; arc_scsi_cmd_done(sc, ccb, error); } } return (ret); } int arc_intr_C(void *arg) { struct arc_softc *sc = arg; struct arc_ccb *ccb = NULL; char *kva = ARC_DMA_KVA(sc->sc_requests); struct arc_io_cmd *cmd; u_int32_t reg, intrstat, obmsg, error; int ret = 0, throttling; intrstat = arc_read(sc, ARC_RC_INTR_STAT); if (!(intrstat & (ARC_RC_INTR_STAT_POSTQUEUE | ARC_RC_INTR_STAT_DOORBELL))) return (ret); if (intrstat & ARC_RC_INTR_STAT_DOORBELL) { ret = 1; if (sc->sc_talking) { /* if an ioctl is talking, wake it up */ arc_write(sc, ARC_RC_INTR_MASK, ~ARC_RC_INTR_MASK_POSTQUEUE); wakeup(sc); } else { /* otherwise drop it */ reg = arc_read(sc, ARC_RC_OUTB_DOORBELL); arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR, reg); if (reg & ARC_RC_I2D_DATA_WRITE_OK) { arc_write(sc, ARC_RC_INB_DOORBELL, ARC_RC_I2D_DATA_READ_OK); } /* if (reg & ARC_RC_I2D_DATA_READ_OK) { arc_write(sc, ARC_RC_INB_DOORBELL, ARC_RC_D2I_DATA_WRITE_OK); } */ if (reg & ARC_RC_I2D_MESSAGE_CMD_DONE) { arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR, ARC_RC_I2D_MSG_CMD_DONE_CLR); obmsg = arc_read(sc, ARC_RC_MSG_RWBUF); if (obmsg == ARC_FWINFO_SIGNATURE_GET_CONFIG) ; /* handle devices hot-plug */ } } } if (intrstat & ARC_RC_INTR_STAT_POSTQUEUE) { ret = 1; throttling = 0; while (arc_read(sc, ARC_RC_INTR_STAT) & ARC_RC_INTR_STAT_POSTQUEUE) { reg = arc_read(sc, ARC_RC_OUTB_REPLYQ_LOW); cmd = (struct arc_io_cmd *)(kva + ((reg & 0xFFFFFFE0) - (u_int32_t)ARC_DMA_DVA(sc->sc_requests))); ccb = cmd->ccb; bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests), ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); error = (reg & ARC_RC_REPLY_QUEUE_ERR); arc_scsi_cmd_done(sc, ccb, error); throttling++; if(throttling == ARC_RC_THROTTLE) { arc_write(sc, ARC_RC_INB_DOORBELL, ARC_RC_D2I_POSTQUEUE_THROTTLING); throttling = 0; } } } return (ret); } static u_int16_t arcmsr_get_doneq_index(struct arc_HBD_Msgu *phbdmu) { u_int16_t doneq_index, index_stripped; doneq_index = phbdmu->doneq_index; if (doneq_index & 0x4000) { index_stripped = doneq_index & 0xFF; index_stripped += 1; index_stripped %= ARCMSR_MAX_HBD_POSTQUEUE; phbdmu->doneq_index = index_stripped ? (index_stripped | 0x4000) : index_stripped; } else { index_stripped = doneq_index; index_stripped += 1; index_stripped %= ARCMSR_MAX_HBD_POSTQUEUE; phbdmu->doneq_index = index_stripped ? index_stripped : (index_stripped | 0x4000); } return (phbdmu->doneq_index); } int arc_intr_D(void *arg) { struct arc_softc *sc = arg; struct arc_ccb *ccb = NULL; char *kva = ARC_DMA_KVA(sc->sc_requests); struct arc_io_cmd *cmd; u_int32_t reg, intrstat, obmsg, error; u_int32_t ob_write_ptr; u_int16_t doneq_index; int ret = 0; struct arc_HBD_Msgu *pmu; intrstat = arc_read(sc, ARC_RD_INTR_STAT); if (!(intrstat & (ARC_RD_INTR_STAT_POSTQUEUE | ARC_RD_INTR_STAT_DOORBELL))) return (ret); if (intrstat & ARC_RD_INTR_STAT_DOORBELL) { ret = 1; if (sc->sc_talking) { /* if an ioctl is talking, wake it up */ arc_write(sc, ARC_RD_INTR_ENABLE, ARC_RD_INTR_ENABLE_POSTQUEUE); wakeup(sc); } else { /* otherwise drop it */ reg = arc_read(sc, ARC_RD_OUTB_DOORBELL); arc_write(sc, ARC_RD_OUTB_DOORBELL, reg); if (reg & ARC_RD_I2D_DATA_WRITE_OK) { arc_write(sc, ARC_RD_INB_DOORBELL, ARC_RD_I2D_DATA_READ_OK); } /* if (reg & ARC_RD_I2D_DATA_READ_OK) { arc_write(sc, ARC_RD_INB_DOORBELL, ARC_RD_D2I_DATA_WRITE_OK); } */ if (reg & ARC_RD_I2D_MESSAGE_CMD_DONE) { arc_write(sc, ARC_RD_OUTB_DOORBELL_CLR, ARC_RD_I2D_MSG_CMD_DONE_CLR); obmsg = arc_read(sc, ARC_RD_MSG_RWBUF); if (obmsg == ARC_FWINFO_SIGNATURE_GET_CONFIG) ; /* handle devices hot-plug */ } } } if (intrstat & ARC_RD_INTR_STAT_POSTQUEUE) { ret = 1; arc_write(sc, ARC_RD_OUTB_INTR_CAUSE, ARC_RD_OUTB_LIST_INT_CLR); bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests), sc->cmdQ_ptr_offset, ARC_MAX_CMDQ_PTR_LEN, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); pmu = sc->pmu; ob_write_ptr = pmu->done_qbuffer[0].addressLow; doneq_index = pmu->doneq_index; while ((doneq_index & 0xFF) != (ob_write_ptr & 0xFF)) { doneq_index = arcmsr_get_doneq_index(pmu); reg = pmu->done_qbuffer[(doneq_index & 0xFF)+1].addressLow; cmd = (struct arc_io_cmd *)(kva + ((reg & 0xFFFFFFF0) - (u_int32_t)ARC_DMA_DVA(sc->sc_requests))); ccb = cmd->ccb; bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests), ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); error = (reg & ARC_RD_REPLY_QUEUE_ERR); arc_scsi_cmd_done(sc, ccb, error); arc_write(sc, ARC_RD_OUTB_READ_PTR, doneq_index); ob_write_ptr = pmu->done_qbuffer[0].addressLow; } } return (ret); } int arc_intr(void *arg) { struct arc_softc *sc = arg; int ret = 0; switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: ret = arc_intr_A(arg); break; case ARC_HBA_TYPE_C: ret = arc_intr_C(arg); break; case ARC_HBA_TYPE_D: ret = arc_intr_D(arg); break; } return (ret); } void arc_scsi_cmd(struct scsi_xfer *xs) { struct scsi_link *link = xs->sc_link; struct arc_softc *sc = link->bus->sb_adapter_softc; struct arc_ccb *ccb; struct arc_msg_scsicmd *cmd; u_int32_t reg, cdb_len; int s; struct arc_HBD_Msgu *pmu; u_int16_t index_stripped; u_int16_t postq_index; struct InBound_SRB *pinbound_srb; if (xs->cmdlen > ARC_MSG_CDBLEN) { bzero(&xs->sense, sizeof(xs->sense)); xs->sense.error_code = SSD_ERRCODE_VALID | 0x70; xs->sense.flags = SKEY_ILLEGAL_REQUEST; xs->sense.add_sense_code = 0x20; xs->error = XS_SENSE; scsi_done(xs); return; } ccb = xs->io; ccb->ccb_xs = xs; if (arc_load_xs(ccb) != 0) { xs->error = XS_DRIVER_STUFFUP; scsi_done(xs); return; } cmd = &ccb->ccb_cmd->cmd; reg = ccb->ccb_cmd_post; ccb->ccb_cmd->ccb = ccb; /* bus is always 0 */ cmd->target = link->target; cmd->lun = link->lun; cmd->function = 1; /* XXX magic number */ cmd->cdb_len = xs->cmdlen; cmd->sgl_len = ccb->ccb_dmamap->dm_nsegs; if (xs->flags & SCSI_DATA_OUT) cmd->flags = ARC_MSG_SCSICMD_FLAG_WRITE; if (ccb->ccb_dmamap->dm_nsegs > ARC_SGL_256LEN) { cmd->flags |= ARC_MSG_SCSICMD_FLAG_SGL_BSIZE_512; /* reg |= ARC_RA_POST_QUEUE_BIGFRAME; */ } cmd->data_len = htole32(xs->datalen); bcopy(&xs->cmd, cmd->cdb, xs->cmdlen); /* we've built the command, let's put it on the hw */ bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests), ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); s = splbio(); switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: if (cmd->flags & ARC_MSG_SCSICMD_FLAG_SGL_BSIZE_512) reg |= ARC_RA_POST_QUEUE_BIGFRAME; arc_write(sc, ARC_RA_POST_QUEUE, reg); break; case ARC_HBA_TYPE_C: cdb_len = sizeof(struct arc_msg_scsicmd) + sizeof(struct arc_sge) * ccb->ccb_dmamap->dm_nsegs; if (cdb_len > 0x300) cdb_len = 0x300; reg = reg | ((cdb_len - 1) >> 6) | 1; if (sc->sc_ccb_phys_hi) arc_write(sc, ARC_RC_INB_POSTQ_HIGH, sc->sc_ccb_phys_hi); arc_write(sc, ARC_RC_INB_POSTQ_LOW, reg); break; case ARC_HBA_TYPE_D: pmu = sc->pmu; postq_index = pmu->postq_index; pinbound_srb = (struct InBound_SRB *)&pmu->post_qbuffer[postq_index & 0xFF]; pinbound_srb->addressHigh = sc->sc_ccb_phys_hi; pinbound_srb->addressLow = ccb->ccb_cmd_post; pinbound_srb->length = ccb->arc_io_cmd_length >> 2; cmd->context = ccb->ccb_cmd_post; if (postq_index & 0x4000) { index_stripped = postq_index & 0xFF; index_stripped += 1; index_stripped %= ARCMSR_MAX_HBD_POSTQUEUE; pmu->postq_index = index_stripped ? (index_stripped | 0x4000) : index_stripped; } else { index_stripped = postq_index; index_stripped += 1; index_stripped %= ARCMSR_MAX_HBD_POSTQUEUE; pmu->postq_index = index_stripped ? index_stripped : (index_stripped | 0x4000); } bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests), sc->cmdQ_ptr_offset, ARC_MAX_CMDQ_PTR_LEN, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); arc_write(sc, ARC_RD_INB_WRITE_PTR, postq_index); break; } if (xs->flags & SCSI_POLL) { if (arc_complete(sc, ccb, xs->timeout) != 0) { xs->error = XS_DRIVER_STUFFUP; scsi_done(xs); } } splx(s); } int arc_load_xs(struct arc_ccb *ccb) { struct arc_softc *sc = ccb->ccb_sc; struct scsi_xfer *xs = ccb->ccb_xs; bus_dmamap_t dmap = ccb->ccb_dmamap; struct arc_sge *sgl = ccb->ccb_cmd->sgl, *sge; u_int64_t addr; int i, error; u_int32_t msg_length; if (xs->datalen == 0) { ccb->arc_io_cmd_length = sizeof(struct arc_msg_scsicmd); ccb->ccb_cmd->cmd.msgPages = 1; return (0); } error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL, (xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); if (error != 0) { printf("%s: error %d loading dmamap\n", DEVNAME(sc), error); return (1); } for (i = 0; i < dmap->dm_nsegs; i++) { sge = &sgl[i]; sge->sg_length = htole32(ARC_SGE_64BIT | dmap->dm_segs[i].ds_len); addr = dmap->dm_segs[i].ds_addr; sge->sg_hi_addr = htole32((u_int32_t)(addr >> 32)); sge->sg_lo_addr = htole32((u_int32_t)addr); } ccb->arc_io_cmd_length = sizeof(struct arc_msg_scsicmd) + sizeof(struct arc_sge) * dmap->dm_nsegs; msg_length = ccb->arc_io_cmd_length; ccb->ccb_cmd->cmd.msgPages = (msg_length/256) + ((msg_length % 256) ? 1 : 0); bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); return (0); } void arc_scsi_cmd_done(struct arc_softc *sc, struct arc_ccb *ccb, u_int32_t error) { struct scsi_xfer *xs = ccb->ccb_xs; struct arc_msg_scsicmd *cmd; if (xs->datalen != 0) { bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, ccb->ccb_dmamap->dm_mapsize, (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); } if (error) { cmd = &ccb->ccb_cmd->cmd; DPRINTF("%s: arc_scsi_cmd_done error! target 0x%x, lun 0x%x, " "status = 0x%x\n", DEVNAME(sc), cmd->target, cmd->lun, cmd->status); DPRINTF("%s: scsi cdb: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x" ", 0x%x, 0x%x, 0x%x\n", DEVNAME(sc), cmd->cdb[0], cmd->cdb[1], cmd->cdb[2], cmd->cdb[3],cmd->cdb[4], cmd->cdb[5], cmd->cdb[6], cmd->cdb[7],cmd->cdb[8], cmd->cdb[9]); switch (cmd->status) { case ARC_MSG_STATUS_SELTIMEOUT: case ARC_MSG_STATUS_ABORTED: case ARC_MSG_STATUS_INIT_FAIL: xs->status = SCSI_OK; xs->error = XS_SELTIMEOUT; break; case SCSI_CHECK: bzero(&xs->sense, sizeof(xs->sense)); bcopy(cmd->sense_data, &xs->sense, min(ARC_MSG_SENSELEN, sizeof(xs->sense))); xs->sense.error_code = SSD_ERRCODE_VALID | 0x70; xs->status = SCSI_CHECK; xs->error = XS_SENSE; xs->resid = 0; break; default: /* unknown device status */ xs->error = XS_BUSY; /* try again later? */ xs->status = SCSI_BUSY; break; } } else { xs->status = SCSI_OK; xs->error = XS_NOERROR; xs->resid = 0; } scsi_done(xs); } int arc_complete(struct arc_softc *sc, struct arc_ccb *nccb, int timeout) { struct arc_ccb *ccb = NULL; char *kva = ARC_DMA_KVA(sc->sc_requests); struct arc_io_cmd *cmd; u_int32_t reg, error, write_ptr; u_int16_t doneq_index; struct arc_HBD_Msgu *phbdmu; int ret = 0; arc_disable_all_intr(sc); do { switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: reg = arc_read(sc, ARC_RA_REPLY_QUEUE); error = (reg & ARC_RA_REPLY_QUEUE_ERR)? 1:0; break; case ARC_HBA_TYPE_C: reg = arc_read(sc, ARC_RC_OUTB_REPLYQ_LOW); error = (reg & ARC_RC_REPLY_QUEUE_ERR); break; case ARC_HBA_TYPE_D: phbdmu = sc->pmu; write_ptr = phbdmu->done_qbuffer[0].addressLow; doneq_index = phbdmu->doneq_index; if((write_ptr & 0xff) == (doneq_index & 0xff)) { Loop0: reg = 0xffffffff; } else { doneq_index = arcmsr_get_doneq_index(phbdmu); reg = phbdmu->done_qbuffer[(doneq_index & 0xFF)+1].addressLow; if (reg == 0) goto Loop0; arc_write(sc, ARC_RD_OUTB_READ_PTR, doneq_index); } error = (reg & ARC_RD_REPLY_QUEUE_ERR); break; } if (reg == 0xffffffff) { if (timeout-- == 0) { return (1); } delay(1000); continue; } switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: cmd = (struct arc_io_cmd *)(kva + ((reg << ARC_RA_REPLY_QUEUE_ADDR_SHIFT) - ARC_DMA_DVA(sc->sc_requests))); break; case ARC_HBA_TYPE_C: case ARC_HBA_TYPE_D: cmd = (struct arc_io_cmd *)(kva + ((reg & 0xFFFFFFE0) - ARC_DMA_DVA(sc->sc_requests))); break; } ccb = cmd->ccb; bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests), ccb->cmd_dma_offset, ARC_MAX_IOCMDLEN, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); arc_scsi_cmd_done(sc, ccb, error); } while (nccb != ccb); arc_enable_all_intr(sc); return (ret); } void arc_enable_all_intr(struct arc_softc *sc) { u_int32_t int_mask; switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: int_mask = arc_read(sc, ARC_RA_INTRMASK); int_mask &= ~(ARC_RA_INTRMASK_POSTQUEUE | ARC_RA_INTRMASK_DOORBELL | ARC_RA_INTRMASK_MSG0); arc_write(sc, ARC_RA_INTRMASK, int_mask); break; case ARC_HBA_TYPE_C: int_mask = arc_read(sc, ARC_RC_INTR_MASK); int_mask &= ~(ARC_RC_INTR_MASK_POSTQUEUE | ARC_RC_INTR_MASK_DOORBELL | ARC_RC_INTR_MASK_UTILITY_A); arc_write(sc, ARC_RC_INTR_MASK, int_mask); break; case ARC_HBA_TYPE_D: int_mask = arc_read(sc, ARC_RD_INTR_ENABLE); int_mask |= ARC_RD_INTR_ENABLE_ALL; arc_write(sc, ARC_RD_INTR_ENABLE, int_mask); break; } } void arc_disable_all_intr(struct arc_softc *sc) { u_int32_t int_mask; switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: int_mask = arc_read(sc, ARC_RA_INTRMASK); int_mask |= ARC_RA_INTR_MASK_ALL; arc_write(sc, ARC_RA_INTRMASK, int_mask); break; case ARC_HBA_TYPE_C: int_mask = arc_read(sc, ARC_RC_INTR_MASK); int_mask |= ARC_RC_INTR_MASK_ALL; arc_write(sc, ARC_RC_INTR_MASK, int_mask); break; case ARC_HBA_TYPE_D: int_mask = arc_read(sc, ARC_RD_INTR_ENABLE); int_mask &= ~ARC_RD_INTR_ENABLE_ALL; arc_write(sc, ARC_RD_INTR_ENABLE, ARC_RD_INTR_DISABLE_ALL); break; } } int arc_map_pci_resources(struct arc_softc *sc, struct pci_attach_args *pa) { pcireg_t memtype; pci_intr_handle_t ih; sc->sc_pc = pa->pa_pc; sc->sc_tag = pa->pa_tag; sc->sc_dmat = pa->pa_dmat; switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, ARC_RA_PCI_BAR); if (pci_mapreg_map(pa, ARC_RA_PCI_BAR, memtype, 0, &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_ios, 0) != 0) { printf(": unable to map ARC_HBA_TYPE_A system" " interface register\n"); return(1); } break; case ARC_HBA_TYPE_C: memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, ARC_RC_PCI_BAR); if (pci_mapreg_map(pa, ARC_RC_PCI_BAR, memtype, 0, &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_ios, 0) != 0) { printf(": unable to map ARC_HBA_TYPE_C system" " interface register\n"); return(1); } break; case ARC_HBA_TYPE_D: memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, ARC_RD_PCI_BAR); if (pci_mapreg_map(pa, ARC_RD_PCI_BAR, memtype, 0, &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_ios, 0) != 0) { printf(": unable to map ARC_HBA_TYPE_D system" " interface register\n"); return(1); } break; } arc_disable_all_intr(sc); if (pci_intr_map(pa, &ih) != 0) { printf(": unable to map interrupt\n"); goto unmap; } sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO, arc_intr, sc, DEVNAME(sc)); if (sc->sc_ih == NULL) { printf(": unable to map interrupt\n"); goto unmap; } printf(": %s\n", pci_intr_string(pa->pa_pc, ih)); return (0); unmap: bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); sc->sc_ios = 0; return (1); } void arc_unmap_pci_resources(struct arc_softc *sc) { pci_intr_disestablish(sc->sc_pc, sc->sc_ih); bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); sc->sc_ios = 0; } int arc_chipA_firmware(struct arc_softc *sc) { struct arc_msg_firmware_info fwinfo; char string[81]; /* sizeof(vendor)*2+1 */ u_int32_t ob_doorbell; if (arc_wait_eq(sc, ARC_RA_OUTB_ADDR1, ARC_RA_OUTB_ADDR1_FIRMWARE_OK, ARC_RA_OUTB_ADDR1_FIRMWARE_OK) != 0) { printf("%s: timeout waiting for firmware ok\n", DEVNAME(sc)); return (1); } if (arc_msg0(sc, ARC_RA_INB_MSG0_GET_CONFIG) != 0) { printf("%s: timeout waiting for get config\n", DEVNAME(sc)); return (1); } arc_read_region(sc, ARC_RA_MSGBUF, &fwinfo, sizeof(fwinfo)); DNPRINTF(ARC_D_INIT, "%s: signature: 0x%08x\n", DEVNAME(sc), letoh32(fwinfo.signature)); if (letoh32(fwinfo.signature) != ARC_FWINFO_SIGNATURE_GET_CONFIG) { printf("%s: invalid firmware info from iop\n", DEVNAME(sc)); return (1); } DNPRINTF(ARC_D_INIT, "%s: request_len: %d\n", DEVNAME(sc), letoh32(fwinfo.request_len)); DNPRINTF(ARC_D_INIT, "%s: queue_len: %d\n", DEVNAME(sc), letoh32(fwinfo.queue_len)); DNPRINTF(ARC_D_INIT, "%s: sdram_size: %d\n", DEVNAME(sc), letoh32(fwinfo.sdram_size)); DNPRINTF(ARC_D_INIT, "%s: sata_ports: %d\n", DEVNAME(sc), letoh32(fwinfo.sata_ports)); scsi_strvis(string, fwinfo.vendor, sizeof(fwinfo.vendor)); DNPRINTF(ARC_D_INIT, "%s: vendor: \"%s\"\n", DEVNAME(sc), string); scsi_strvis(string, fwinfo.model, sizeof(fwinfo.model)); DNPRINTF(ARC_D_INIT, "%s: model: \"%s\"\n", DEVNAME(sc), string); scsi_strvis(string, fwinfo.fw_version, sizeof(fwinfo.fw_version)); DNPRINTF(ARC_D_INIT, "%s: firmware: \"%s\"\n", DEVNAME(sc), string); if (letoh32(fwinfo.request_len) != ARC_MAX_IOCMDLEN) { printf("%s: unexpected request frame size (%d != %d)\n", DEVNAME(sc), letoh32(fwinfo.request_len), ARC_MAX_IOCMDLEN); return (1); } sc->sc_req_count = letoh32(fwinfo.queue_len); if (arc_msg0(sc, ARC_RA_INB_MSG0_START_BGRB) != 0) { printf("%s: timeout waiting to start bg rebuild\n", DEVNAME(sc)); return (1); } /* clear doorbell buffer */ ob_doorbell = arc_read(sc, ARC_RA_OUTB_DOORBELL); arc_write(sc, ARC_RA_OUTB_DOORBELL, ob_doorbell); arc_write(sc, ARC_RA_INB_DOORBELL, ARC_RA_INB_DOORBELL_READ_OK); printf("%s: %d ports, %dMB SDRAM, firmware %s\n", DEVNAME(sc), letoh32(fwinfo.sata_ports), letoh32(fwinfo.sdram_size), string); return (0); } int arc_chipB_firmware(struct arc_softc *sc) { if (arc_wait_eq(sc, ARC_RB_IOP2DRV_DOORBELL, ARC_RA_OUTB_ADDR1_FIRMWARE_OK, ARC_RA_OUTB_ADDR1_FIRMWARE_OK) != 0) { printf("%s: timeout waiting for firmware ok\n", DEVNAME(sc)); return (1); } return (1); } int arc_chipC_firmware(struct arc_softc *sc) { struct arc_msg_firmware_info fwinfo; char string[81]; /* sizeof(vendor)*2+1 */ u_int32_t ob_doorbell; if (arc_wait_eq(sc, ARC_RC_OUTB_MSGADDR1, ARC_RC_OUTB_MSG_FIRMWARE_OK, ARC_RC_OUTB_MSG_FIRMWARE_OK) != 0) { printf("%s: timeout waiting for firmware ok\n", DEVNAME(sc)); return (1); } if (arc_msg0(sc, ARC_RC_INB_MSG0_GET_CONFIG) != 0) { printf("%s: timeout waiting for get config\n", DEVNAME(sc)); return (1); } arc_read_region(sc, ARC_RC_MSG_RWBUF, &fwinfo, sizeof(fwinfo)); DNPRINTF(ARC_D_INIT, "%s: signature: 0x%08x\n", DEVNAME(sc), letoh32(fwinfo.signature)); if (letoh32(fwinfo.signature) != ARC_FWINFO_SIGNATURE_GET_CONFIG) { printf("%s: invalid firmware info from iop\n", DEVNAME(sc)); return (1); } DNPRINTF(ARC_D_INIT, "%s: request_len: %d\n", DEVNAME(sc), letoh32(fwinfo.request_len)); DNPRINTF(ARC_D_INIT, "%s: queue_len: %d\n", DEVNAME(sc), letoh32(fwinfo.queue_len)); DNPRINTF(ARC_D_INIT, "%s: sdram_size: %d\n", DEVNAME(sc), letoh32(fwinfo.sdram_size)); DNPRINTF(ARC_D_INIT, "%s: sata_ports: %d\n", DEVNAME(sc), letoh32(fwinfo.sata_ports)); scsi_strvis(string, fwinfo.vendor, sizeof(fwinfo.vendor)); DNPRINTF(ARC_D_INIT, "%s: vendor: \"%s\"\n", DEVNAME(sc), string); scsi_strvis(string, fwinfo.model, sizeof(fwinfo.model)); DNPRINTF(ARC_D_INIT, "%s: model: \"%s\"\n", DEVNAME(sc), string); scsi_strvis(string, fwinfo.fw_version, sizeof(fwinfo.fw_version)); DNPRINTF(ARC_D_INIT, "%s: firmware: \"%s\"\n", DEVNAME(sc), string); if (letoh32(fwinfo.request_len) != ARC_MAX_IOCMDLEN) { printf("%s: unexpected request frame size (%d != %d)\n", DEVNAME(sc), letoh32(fwinfo.request_len), ARC_MAX_IOCMDLEN); return (1); } sc->sc_req_count = letoh32(fwinfo.queue_len); if (arc_msg0(sc, ARC_RC_INB_MSG0_START_BGRB) != 0) { printf("%s: timeout waiting to start bg rebuild\n", DEVNAME(sc)); return (1); } /* clear doorbell buffer */ ob_doorbell = arc_read(sc, ARC_RC_OUTB_DOORBELL); arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR, ob_doorbell); arc_write(sc, ARC_RC_INB_DOORBELL, ARC_RC_D2I_DATA_READ_OK); printf("%s: %d ports, %dMB SDRAM, firmware %s\n", DEVNAME(sc), letoh32(fwinfo.sata_ports), letoh32(fwinfo.sdram_size), string); return (0); } int arc_chipD_firmware(struct arc_softc *sc) { struct arc_msg_firmware_info fwinfo; char string[81]; /* sizeof(vendor)*2+1 */ u_int32_t ob_doorbell; if (arc_wait_eq(sc, ARC_RD_OUTB_MSGADDR1, ARC_RD_OUTB_MSG_FIRMWARE_OK, ARC_RD_OUTB_MSG_FIRMWARE_OK) != 0) { printf("%s: timeout waiting for firmware ok\n", DEVNAME(sc)); return (1); } if ((arc_read(sc, ARC_RD_OUTB_DOORBELL) & ARC_RD_I2D_MESSAGE_CMD_DONE)) arc_write(sc, ARC_RD_OUTB_DOORBELL, ARC_RD_I2D_MESSAGE_CMD_DONE_CLEAR); if (arc_msg0(sc, ARC_RD_INB_MSG0_GET_CONFIG) != 0) { printf("%s: timeout waiting for get config\n", DEVNAME(sc)); return (1); } arc_read_region(sc, ARC_RD_MSG_RWBUF, &fwinfo, sizeof(fwinfo)); DNPRINTF(ARC_D_INIT, "%s: signature: 0x%08x\n", DEVNAME(sc), letoh32(fwinfo.signature)); if (letoh32(fwinfo.signature) != ARC_FWINFO_SIGNATURE_GET_CONFIG) { printf("%s: invalid firmware info from iop\n", DEVNAME(sc)); return (1); } DNPRINTF(ARC_D_INIT, "%s: request_len: %d\n", DEVNAME(sc), letoh32(fwinfo.request_len)); DNPRINTF(ARC_D_INIT, "%s: queue_len: %d\n", DEVNAME(sc), letoh32(fwinfo.queue_len)); DNPRINTF(ARC_D_INIT, "%s: sdram_size: %d\n", DEVNAME(sc), letoh32(fwinfo.sdram_size)); DNPRINTF(ARC_D_INIT, "%s: sata_ports: %d\n", DEVNAME(sc), letoh32(fwinfo.sata_ports)); scsi_strvis(string, fwinfo.vendor, sizeof(fwinfo.vendor)); DNPRINTF(ARC_D_INIT, "%s: vendor: \"%s\"\n", DEVNAME(sc), string); scsi_strvis(string, fwinfo.model, sizeof(fwinfo.model)); DNPRINTF(ARC_D_INIT, "%s: model: \"%s\"\n", DEVNAME(sc), string); scsi_strvis(string, fwinfo.fw_version, sizeof(fwinfo.fw_version)); DNPRINTF(ARC_D_INIT, "%s: firmware: \"%s\"\n", DEVNAME(sc), string); if (letoh32(fwinfo.request_len) != ARC_MAX_IOCMDLEN) { printf("%s: unexpected request frame size (%d != %d)\n", DEVNAME(sc), letoh32(fwinfo.request_len), ARC_MAX_IOCMDLEN); return (1); } sc->sc_req_count = letoh32(fwinfo.queue_len) - 1; if (arc_msg0(sc, ARC_RD_INB_MSG0_START_BGRB) != 0) { printf("%s: timeout waiting to start bg rebuild\n", DEVNAME(sc)); return (1); } /* clear doorbell buffer */ ob_doorbell = arc_read(sc, ARC_RD_OUTB_DOORBELL); arc_write(sc, ARC_RD_OUTB_DOORBELL_CLR, ob_doorbell); arc_write(sc, ARC_RD_INB_DOORBELL, ARC_RD_D2I_DATA_READ_OK); printf("%s: %d ports, %dMB SDRAM, firmware %s\n", DEVNAME(sc), letoh32(fwinfo.sata_ports), letoh32(fwinfo.sdram_size), string); return (0); } void arc_stop_bgrb_proc(struct arc_softc *sc) { switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: if (arc_msg0(sc, ARC_RA_INB_MSG0_STOP_BGRB) != 0) printf("%s: timeout waiting to stop bg rebuild\n", DEVNAME(sc)); break; case ARC_HBA_TYPE_C: if (arc_msg0(sc, ARC_RC_INB_MSG0_STOP_BGRB) != 0) printf("%s: timeout waiting to stop bg rebuild\n", DEVNAME(sc)); break; case ARC_HBA_TYPE_D: if (arc_msg0(sc, ARC_RD_INB_MSG0_STOP_BGRB) != 0) printf("%s: timeout waiting to stop bg rebuild\n", DEVNAME(sc)); break; } } void arc_flush_cache(struct arc_softc *sc) { switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: if (arc_msg0(sc, ARC_RA_INB_MSG0_FLUSH_CACHE) != 0) printf("%s: timeout waiting to flush cache\n", DEVNAME(sc)); break; case ARC_HBA_TYPE_C: if (arc_msg0(sc, ARC_RC_INB_MSG0_FLUSH_CACHE) != 0) printf("%s: timeout waiting to flush cache\n", DEVNAME(sc)); break; case ARC_HBA_TYPE_D: if (arc_msg0(sc, ARC_RD_INB_MSG0_FLUSH_CACHE) != 0) printf("%s: timeout waiting to flush cache\n", DEVNAME(sc)); break; } } void arc_iop_set_conf(struct arc_softc *sc) { u_int32_t ccb_phys_hi; struct arc_HBD_Msgu *phbdmu; ccb_phys_hi = sc->sc_ccb_phys_hi; switch (sc->sc_adp_type) { case ARC_HBA_TYPE_A: arc_write(sc, ARC_RA_MSGBUF, ARC_FWINFO_SIGNATURE_SET_CONFIG); arc_write(sc, ARC_RA_MSGBUF+1, ccb_phys_hi); arc_msg0(sc, ARC_RA_INB_MSG0_SET_CONFIG); break; case ARC_HBA_TYPE_C: arc_write(sc, ARC_RC_MSG_RWBUF, ARC_FWINFO_SIGNATURE_SET_CONFIG); arc_write(sc, ARC_RC_MSG_RWBUF+1, ccb_phys_hi); arc_msg0(sc, ARC_RC_INB_MSG0_SET_CONFIG); break; case ARC_HBA_TYPE_D: phbdmu = sc->pmu; phbdmu->postq_index = 0; phbdmu->doneq_index = 0x40FF; arc_write(sc, ARC_RD_MSG_RWBUF, ARC_FWINFO_SIGNATURE_SET_CONFIG); arc_write(sc, ARC_RD_MSG_RWBUF+4, ccb_phys_hi); arc_write(sc, ARC_RD_MSG_RWBUF+8, sc->postQ_buffer); arc_write(sc, ARC_RD_MSG_RWBUF+12, sc->doneQ_buffer); arc_write(sc, ARC_RD_MSG_RWBUF+16, 0x100); arc_msg0(sc, ARC_RD_INB_MSG0_SET_CONFIG); break; } } #if NBIO > 0 int arc_bioctl(struct device *self, u_long cmd, caddr_t addr) { struct arc_softc *sc = (struct arc_softc *)self; int error = 0; DPRINTF("%s: arc_bioctl\n", DEVNAME(sc)); switch (cmd) { case BIOCINQ: error = arc_bio_inq(sc, (struct bioc_inq *)addr); break; case BIOCVOL: error = arc_bio_vol(sc, (struct bioc_vol *)addr); break; case BIOCDISK: error = arc_bio_disk(sc, (struct bioc_disk *)addr); break; case BIOCALARM: error = arc_bio_alarm(sc, (struct bioc_alarm *)addr); break; case BIOCBLINK: error = arc_bio_blink(sc, (struct bioc_blink *)addr); break; default: error = ENOTTY; break; } return (error); } int arc_bio_alarm(struct arc_softc *sc, struct bioc_alarm *ba) { u_int8_t request[2]; u_int8_t reply[1]; size_t len; int error = 0; DPRINTF("%s: arc_bio_alarm\n", DEVNAME(sc)); switch (ba->ba_opcode) { case BIOC_SAENABLE: case BIOC_SADISABLE: request[0] = ARC_FW_SET_ALARM; request[1] = (ba->ba_opcode == BIOC_SAENABLE) ? ARC_FW_SET_ALARM_ENABLE : ARC_FW_SET_ALARM_DISABLE; len = sizeof(request); break; case BIOC_SASILENCE: request[0] = ARC_FW_MUTE_ALARM; len = 1; break; case BIOC_GASTATUS: /* system info is too big/ugly to deal with here */ return (arc_bio_alarm_state(sc, ba)); default: return (EOPNOTSUPP); } arc_lock(sc); error = arc_msgbuf(sc, request, len, reply, sizeof(reply), 0); arc_unlock(sc); if (error != 0) return (error); switch (reply[0]) { case ARC_FW_CMD_OK: return (0); case ARC_FW_CMD_PASS_REQD: return (EPERM); default: return (EIO); } } int arc_bio_alarm_state(struct arc_softc *sc, struct bioc_alarm *ba) { u_int8_t request = ARC_FW_SYSINFO; struct arc_fw_sysinfo *sysinfo; int error = 0; sysinfo = malloc(sizeof(struct arc_fw_sysinfo), M_TEMP, M_WAITOK); request = ARC_FW_SYSINFO; arc_lock(sc); error = arc_msgbuf(sc, &request, sizeof(request), sysinfo, sizeof(struct arc_fw_sysinfo), 0); arc_unlock(sc); if (error != 0) goto out; ba->ba_status = sysinfo->alarm; out: free(sysinfo, M_TEMP, sizeof *sysinfo); return (error); } int arc_bio_inq(struct arc_softc *sc, struct bioc_inq *bi) { u_int8_t request[2]; struct arc_fw_sysinfo *sysinfo; struct arc_fw_volinfo *volinfo; int maxvols, nvols = 0, i; int error = 0; char string[20]; DPRINTF("%s: arc_bio_inq\n", DEVNAME(sc)); sysinfo = malloc(sizeof(struct arc_fw_sysinfo), M_TEMP, M_WAITOK); volinfo = malloc(sizeof(struct arc_fw_volinfo), M_TEMP, M_WAITOK); arc_lock(sc); request[0] = ARC_FW_SYSINFO; error = arc_msgbuf(sc, request, 1, sysinfo, sizeof(struct arc_fw_sysinfo), 0); if (error != 0) { DPRINTF("%s: arc_bio_inq get sysinfo failed!\n", DEVNAME(sc)); goto out; } maxvols = sysinfo->max_volume_set; request[0] = ARC_FW_VOLINFO; for (i = 0; i < maxvols; i++) { request[1] = i; error = arc_msgbuf(sc, request, sizeof(request), volinfo, sizeof(struct arc_fw_volinfo), 0); if (error != 0) { DPRINTF("%s: arc_bio_inq get volinfo failed!\n", DEVNAME(sc)); goto out; } /* * I can't find an easy way to see if the volume exists or not * except to say that if it has no capacity then it isn't there. * Ignore passthru volumes, bioc_vol doesn't understand them. */ if ((volinfo->capacity != 0 || volinfo->capacity2 != 0) && volinfo->raid_level != ARC_FW_VOL_RAIDLEVEL_PASSTHRU) { nvols++; scsi_strvis(string, volinfo->set_name, 16); DPRINTF("%s: volume set: \"%s\"\n", DEVNAME(sc), string); } } strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev)); bi->bi_novol = nvols; DPRINTF("%s: volume set number = %d\n", DEVNAME(sc), nvols); out: arc_unlock(sc); free(volinfo, M_TEMP, sizeof *volinfo); free(sysinfo, M_TEMP, sizeof *sysinfo); return (error); } int arc_bio_blink(struct arc_softc *sc, struct bioc_blink *blink) { u_int8_t request[6]; u_int32_t mask; int error = 0; DPRINTF("%s: arc_bio_blink\n", DEVNAME(sc)); request[0] = ARC_FW_BLINK; request[1] = ARC_FW_BLINK_ENABLE; switch (blink->bb_status) { case BIOC_SBUNBLINK: sc->sc_ledmask &= ~(1 << blink->bb_target); break; case BIOC_SBBLINK: sc->sc_ledmask |= (1 << blink->bb_target); break; default: return (EINVAL); } mask = htole32(sc->sc_ledmask); bcopy(&mask, &request[2], 4); arc_lock(sc); error = arc_msgbuf(sc, request, sizeof(request), NULL, 0, 0); arc_unlock(sc); if (error) return (EIO); return (0); } int arc_bio_getvol(struct arc_softc *sc, int vol, struct arc_fw_volinfo *volinfo) { u_int8_t request[2]; struct arc_fw_sysinfo *sysinfo; int error = 0; int maxvols, nvols = 0, i; DPRINTF("%s: arc_bio_getvol\n", DEVNAME(sc)); sysinfo = malloc(sizeof(struct arc_fw_sysinfo), M_TEMP, M_WAITOK); request[0] = ARC_FW_SYSINFO; error = arc_msgbuf(sc, request, 1, sysinfo, sizeof(struct arc_fw_sysinfo), 0); if (error != 0) goto out; maxvols = sysinfo->max_volume_set; request[0] = ARC_FW_VOLINFO; for (i = 0; i < maxvols; i++) { request[1] = i; error = arc_msgbuf(sc, request, sizeof(request), volinfo, sizeof(struct arc_fw_volinfo), 0); if (error != 0) goto out; if ((volinfo->capacity == 0 && volinfo->capacity2 == 0) || volinfo->raid_level == ARC_FW_VOL_RAIDLEVEL_PASSTHRU) continue; if (nvols == vol) break; nvols++; } if (nvols != vol || (volinfo->capacity == 0 && volinfo->capacity2 == 0) || volinfo->raid_level == ARC_FW_VOL_RAIDLEVEL_PASSTHRU) { error = ENODEV; goto out; } out: free(sysinfo, M_TEMP, sizeof *sysinfo); return (error); } int arc_bio_vol(struct arc_softc *sc, struct bioc_vol *bv) { struct arc_fw_volinfo *volinfo; struct scsi_link *sc_link; struct device *dev; u_int64_t blocks; u_int32_t status; int error = 0; DPRINTF("%s: arc_bio_vol\n", DEVNAME(sc)); volinfo = malloc(sizeof(struct arc_fw_volinfo), M_TEMP, M_WAITOK); arc_lock(sc); error = arc_bio_getvol(sc, bv->bv_volid, volinfo); arc_unlock(sc); if (error != 0) goto out; bv->bv_percent = -1; bv->bv_seconds = 0; status = letoh32(volinfo->volume_status); if (status == 0x0) { if (letoh32(volinfo->fail_mask) == 0x0) bv->bv_status = BIOC_SVONLINE; else bv->bv_status = BIOC_SVDEGRADED; } else if (status & ARC_FW_VOL_STATUS_NEED_REGEN) bv->bv_status = BIOC_SVDEGRADED; else if (status & ARC_FW_VOL_STATUS_FAILED) bv->bv_status = BIOC_SVOFFLINE; else if (status & ARC_FW_VOL_STATUS_INITTING) { bv->bv_status = BIOC_SVBUILDING; bv->bv_percent = letoh32(volinfo->progress) / 10; } else if (status & ARC_FW_VOL_STATUS_REBUILDING) { bv->bv_status = BIOC_SVREBUILD; bv->bv_percent = letoh32(volinfo->progress) / 10; } blocks = (u_int64_t)letoh32(volinfo->capacity2) << 32; blocks += (u_int64_t)letoh32(volinfo->capacity); bv->bv_size = blocks * ARC_BLOCKSIZE; /* XXX */ switch (volinfo->raid_level) { case ARC_FW_VOL_RAIDLEVEL_0: bv->bv_level = 0; break; case ARC_FW_VOL_RAIDLEVEL_1: bv->bv_level = 1; break; case ARC_FW_VOL_RAIDLEVEL_3: bv->bv_level = 3; break; case ARC_FW_VOL_RAIDLEVEL_5: bv->bv_level = 5; break; case ARC_FW_VOL_RAIDLEVEL_6: bv->bv_level = 6; break; case ARC_FW_VOL_RAIDLEVEL_PASSTHRU: default: bv->bv_level = -1; break; } bv->bv_nodisk = volinfo->member_disks; sc_link = scsi_get_link(sc->sc_scsibus, volinfo->scsi_attr.target, volinfo->scsi_attr.lun); if (sc_link != NULL) { dev = sc_link->device_softc; strlcpy(bv->bv_dev, dev->dv_xname, sizeof(bv->bv_dev)); } out: free(volinfo, M_TEMP, sizeof *volinfo); return (error); } int arc_bio_disk(struct arc_softc *sc, struct bioc_disk *bd) { u_int8_t request[2]; struct arc_fw_volinfo *volinfo; struct arc_fw_raidinfo *raidinfo; struct arc_fw_diskinfo *diskinfo; int error = 0; u_int64_t blocks; char model[81]; char serial[41]; char rev[17]; DPRINTF("%s: arc_bio_disk\n", DEVNAME(sc)); volinfo = malloc(sizeof(struct arc_fw_volinfo), M_TEMP, M_WAITOK); raidinfo = malloc(sizeof(struct arc_fw_raidinfo), M_TEMP, M_WAITOK); diskinfo = malloc(sizeof(struct arc_fw_diskinfo), M_TEMP, M_WAITOK); arc_lock(sc); error = arc_bio_getvol(sc, bd->bd_volid, volinfo); if (error != 0) goto out; request[0] = ARC_FW_RAIDINFO; request[1] = volinfo->raid_set_number; error = arc_msgbuf(sc, request, sizeof(request), raidinfo, sizeof(struct arc_fw_raidinfo), 0); if (error != 0) goto out; if (bd->bd_diskid > raidinfo->member_devices) { error = ENODEV; goto out; } if (raidinfo->device_array[bd->bd_diskid] == 0xff) { /* * the disk doesn't exist anymore. bio is too dumb to be * able to display that, so put it on another bus */ bd->bd_channel = 1; bd->bd_target = 0; bd->bd_lun = 0; bd->bd_status = BIOC_SDOFFLINE; strlcpy(bd->bd_vendor, "disk missing", sizeof(bd->bd_vendor)); goto out; } request[0] = ARC_FW_DISKINFO; request[1] = raidinfo->device_array[bd->bd_diskid]; error = arc_msgbuf(sc, request, sizeof(request), diskinfo, sizeof(struct arc_fw_diskinfo), 1); if (error != 0) goto out; #if 0 bd->bd_channel = diskinfo->scsi_attr.channel; bd->bd_target = diskinfo->scsi_attr.target; bd->bd_lun = diskinfo->scsi_attr.lun; #endif /* * the firmware doesn't seem to fill scsi_attr in, so fake it with * the diskid. */ bd->bd_channel = 0; bd->bd_target = raidinfo->device_array[bd->bd_diskid]; bd->bd_lun = 0; bd->bd_status = BIOC_SDONLINE; blocks = (u_int64_t)letoh32(diskinfo->capacity2) << 32; blocks += (u_int64_t)letoh32(diskinfo->capacity); bd->bd_size = blocks * ARC_BLOCKSIZE; /* XXX */ scsi_strvis(model, diskinfo->model, sizeof(diskinfo->model)); scsi_strvis(serial, diskinfo->serial, sizeof(diskinfo->serial)); scsi_strvis(rev, diskinfo->firmware_rev, sizeof(diskinfo->firmware_rev)); snprintf(bd->bd_vendor, sizeof(bd->bd_vendor), "%s %s", model, rev); strlcpy(bd->bd_serial, serial, sizeof(bd->bd_serial)); out: arc_unlock(sc); free(diskinfo, M_TEMP, sizeof *diskinfo); free(raidinfo, M_TEMP, sizeof *raidinfo); free(volinfo, M_TEMP, sizeof *volinfo); return (error); } u_int8_t arc_msg_cksum(void *cmd, u_int16_t len) { u_int8_t *buf = cmd; u_int8_t cksum; int i; cksum = (u_int8_t)(len >> 8) + (u_int8_t)len; for (i = 0; i < len; i++) cksum += buf[i]; return (cksum); } int arc_msgbuf(struct arc_softc *sc, void *wptr, size_t wbuflen, void *rptr, size_t rbuflen, int sreadok) { u_int8_t rwbuf[ARC_RA_IOC_RWBUF_MAXLEN]; u_int8_t *wbuf, *rbuf, cksum; int wlen, wdone = 0, rlen, rdone = 0; u_int16_t rlenhdr = 0; struct arc_fw_bufhdr *bufhdr; u_int32_t reg, rwlen, write_ok, read_ok; int error = 0; #ifdef ARC_DEBUG int i; #endif DPRINTF("%s: arc_msgbuf wbuflen: %zu rbuflen: %zu\n", DEVNAME(sc), wbuflen, rbuflen); switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: reg = arc_read(sc, ARC_RA_OUTB_DOORBELL); break; case ARC_HBA_TYPE_C: reg = arc_read(sc, ARC_RC_OUTB_DOORBELL); break; case ARC_HBA_TYPE_D: reg = arc_read(sc, ARC_RD_OUTB_DOORBELL); break; } /* if (reg) return (EBUSY); */ wlen = sizeof(struct arc_fw_bufhdr) + wbuflen + 1; /* 1 for cksum */ wbuf = malloc(wlen, M_TEMP, M_WAITOK); rlen = sizeof(struct arc_fw_bufhdr) + rbuflen + 1; /* 1 for cksum */ rbuf = malloc(rlen, M_TEMP, M_WAITOK); DNPRINTF(ARC_D_DB, "%s: arc_msgbuf wlen: %d rlen: %d\n", DEVNAME(sc), wlen, rlen); bufhdr = (struct arc_fw_bufhdr *)wbuf; bufhdr->hdr = arc_fw_hdr; bufhdr->len = htole16(wbuflen); bcopy(wptr, wbuf + sizeof(struct arc_fw_bufhdr), wbuflen); wbuf[wlen - 1] = arc_msg_cksum(wptr, wbuflen); /* reg = ARC_RA_OUTB_DOORBELL_READ_OK; */ read_ok = 1; do { if ((read_ok) && wdone < wlen) { bzero(rwbuf, sizeof(rwbuf)); rwlen = (wlen - wdone) % sizeof(rwbuf); bcopy(&wbuf[wdone], rwbuf, rwlen); #ifdef ARC_DEBUG if (arcdebug & ARC_D_DB) { printf("%s: write %d:", DEVNAME(sc), rwlen); for (i = 0; i < rwlen; i++) printf(" 0x%02x", rwbuf[i]); printf("\n"); } #endif switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: /* copy the chunk to the hw */ arc_write(sc, ARC_RA_IOC_WBUF_LEN, rwlen); arc_write_region(sc, ARC_RA_IOC_WBUF, rwbuf, sizeof(rwbuf)); /* say we have a buffer for the hw */ arc_write(sc, ARC_RA_INB_DOORBELL, ARC_RA_INB_DOORBELL_WRITE_OK); break; case ARC_HBA_TYPE_C: /* copy the chunk to the hw */ arc_write(sc, ARC_RC_MSG_WBUF_LEN, rwlen); arc_write_region(sc, ARC_RC_MSG_WBUF, rwbuf, sizeof(rwbuf)); /* say we have a buffer for the hw */ arc_write(sc, ARC_RC_INB_DOORBELL, ARC_RC_D2I_DATA_WRITE_OK); break; case ARC_HBA_TYPE_D: /* copy the chunk to the hw */ arc_write(sc, ARC_RD_MSG_WBUF_LEN, rwlen); arc_write_region(sc, ARC_RD_MSG_WBUF, rwbuf, sizeof(rwbuf)); /* say we have a buffer for the hw */ arc_write(sc, ARC_RD_INB_DOORBELL, ARC_RD_D2I_DATA_WRITE_OK); break; } wdone += rwlen; } if (rptr == NULL) goto out; switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: while ((reg = arc_read(sc, ARC_RA_OUTB_DOORBELL)) == 0) arc_wait(sc); arc_write(sc, ARC_RA_OUTB_DOORBELL, reg); write_ok = reg & ARC_RA_OUTB_DOORBELL_WRITE_OK; read_ok = reg & ARC_RA_OUTB_DOORBELL_READ_OK; break; case ARC_HBA_TYPE_C: while ((reg = arc_read(sc, ARC_RC_OUTB_DOORBELL)) == 0) arc_wait(sc); arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR, reg); write_ok = reg & ARC_RC_I2D_DATA_WRITE_OK; read_ok = reg & ARC_RC_I2D_DATA_READ_OK; break; case ARC_HBA_TYPE_D: while ((reg = arc_read(sc, ARC_RD_OUTB_DOORBELL)) == 0) arc_wait(sc); arc_write(sc, ARC_RD_OUTB_DOORBELL_CLR, reg); write_ok = reg & ARC_RD_I2D_DATA_WRITE_OK; read_ok = reg & ARC_RD_I2D_DATA_READ_OK; break; } DNPRINTF(ARC_D_DB, "%s: reg: 0x%08x\n", DEVNAME(sc), reg); if ((write_ok) && rdone < rlen) { switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: rwlen = arc_read(sc, ARC_RA_IOC_RBUF_LEN); break; case ARC_HBA_TYPE_C: rwlen = arc_read(sc, ARC_RC_MSG_RBUF_LEN); break; case ARC_HBA_TYPE_D: rwlen = arc_read(sc, ARC_RD_MSG_RBUF_LEN); break; } if (rwlen > sizeof(rwbuf)) { DNPRINTF(ARC_D_DB, "%s: rwlen too big\n", DEVNAME(sc)); error = EIO; goto out; } switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: arc_read_region(sc, ARC_RA_IOC_RBUF, rwbuf, sizeof(rwbuf)); arc_write(sc, ARC_RA_INB_DOORBELL, ARC_RA_INB_DOORBELL_READ_OK); break; case ARC_HBA_TYPE_C: arc_read_region(sc, ARC_RC_MSG_RBUF, rwbuf, sizeof(rwbuf)); arc_write(sc, ARC_RC_INB_DOORBELL, ARC_RC_I2D_DATA_READ_OK); break; case ARC_HBA_TYPE_D: arc_read_region(sc, ARC_RD_MSG_RBUF, rwbuf, sizeof(rwbuf)); arc_write(sc, ARC_RD_INB_DOORBELL, ARC_RD_I2D_DATA_READ_OK); break; } if ((rlen > 3) && (rdone == 3)) { rlen = *(u_int16_t *)rwbuf; rlen = sizeof(struct arc_fw_bufhdr) + rlen + 1; } #ifdef ARC_DEBUG printf("%s: len: %d+%d=%d/%d\n", DEVNAME(sc), rwlen, rdone, rwlen + rdone, rlen); if (arcdebug & ARC_D_DB) { printf("%s: read:", DEVNAME(sc)); for (i = 0; i < rwlen; i++) printf(" 0x%02x", rwbuf[i]); printf("\n"); } #endif if ((rdone + rwlen) > rlen) { DNPRINTF(ARC_D_DB, "%s: rwbuf too big\n", DEVNAME(sc)); error = EIO; goto out; } bcopy(rwbuf, &rbuf[rdone], rwlen); rdone += rwlen; /* * Allow for short reads, by reading the length * value from the response header and shrinking our * idea of size, if required. * This deals with the growth of diskinfo struct from * 128 to 132 bytes. */ if (sreadok && rdone >= sizeof(struct arc_fw_bufhdr) && rlenhdr == 0) { bufhdr = (struct arc_fw_bufhdr *)rbuf; rlenhdr = letoh16(bufhdr->len); if (rlenhdr < rbuflen) { rbuflen = rlenhdr; rlen = sizeof(struct arc_fw_bufhdr) + rbuflen + 1; /* 1 for cksum */ } } } } while (rdone != rlen); bufhdr = (struct arc_fw_bufhdr *)rbuf; if (memcmp(&bufhdr->hdr, &arc_fw_hdr, sizeof(bufhdr->hdr)) != 0) { DNPRINTF(ARC_D_DB, "%s: rbuf hdr is wrong\n", DEVNAME(sc)); error = EIO; goto out; } if (bufhdr->len != htole16(rbuflen)) { DNPRINTF(ARC_D_DB, "%s: get_len: 0x%x, req_len: 0x%zu\n", DEVNAME(sc), bufhdr->len, rbuflen); } bcopy(rbuf + sizeof(struct arc_fw_bufhdr), rptr, bufhdr->len); cksum = arc_msg_cksum(rptr, bufhdr->len); if (rbuf[rlen - 1] != cksum) { DNPRINTF(ARC_D_DB, "%s: invalid cksum, got :0x%x, calculated:" " 0x%x\n", DEVNAME(sc), rbuf[rlen-1], cksum); error = EIO; goto out; } out: free(wbuf, M_TEMP, 0); free(rbuf, M_TEMP, 0); return (error); } void arc_lock(struct arc_softc *sc) { int s; u_int32_t int_mask; rw_enter_write(&sc->sc_lock); s = splbio(); switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: int_mask = arc_read(sc, ARC_RA_INTRMASK) | ARC_RA_INTRMASK_DOORBELL; arc_write(sc, ARC_RA_INTRMASK, int_mask); break; case ARC_HBA_TYPE_C: int_mask = arc_read(sc, ARC_RC_INTR_MASK) | ARC_RC_INTR_MASK_DOORBELL; arc_write(sc, ARC_RC_INTR_MASK, int_mask); break; case ARC_HBA_TYPE_D: int_mask = arc_read(sc, ARC_RD_INTR_ENABLE) & ~ARC_RD_INTR_ENABLE_DOORBELL; arc_write(sc, ARC_RD_INTR_ENABLE, int_mask); break; } sc->sc_talking = 1; splx(s); } void arc_unlock(struct arc_softc *sc) { int s; u_int32_t int_mask; s = splbio(); sc->sc_talking = 0; switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: int_mask = arc_read(sc, ARC_RA_INTRMASK) & ~ARC_RA_INTRMASK_DOORBELL; arc_write(sc, ARC_RA_INTRMASK, int_mask); break; case ARC_HBA_TYPE_C: int_mask = arc_read(sc, ARC_RC_INTR_MASK) & ~ARC_RC_INTR_MASK_DOORBELL; arc_write(sc, ARC_RC_INTR_MASK, int_mask); break; case ARC_HBA_TYPE_D: int_mask = arc_read(sc, ARC_RD_INTR_ENABLE) | ARC_RD_INTR_ENABLE_DOORBELL; arc_write(sc, ARC_RD_INTR_ENABLE, int_mask); break; } splx(s); rw_exit_write(&sc->sc_lock); } void arc_wait(struct arc_softc *sc) { int error, s; u_int32_t int_mask; s = splbio(); switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: int_mask = arc_read(sc, ARC_RA_INTRMASK) & ~ARC_RA_INTRMASK_DOORBELL; arc_write(sc, ARC_RA_INTRMASK, int_mask); error = tsleep_nsec(sc, PWAIT, "arcdb", SEC_TO_NSEC(1)); if (error == EWOULDBLOCK) { int_mask = arc_read(sc, ARC_RA_INTRMASK) | ARC_RA_INTRMASK_DOORBELL; arc_write(sc, ARC_RA_INTRMASK, int_mask); } break; case ARC_HBA_TYPE_C: int_mask = arc_read(sc, ARC_RC_INTR_MASK) & ~ARC_RC_INTR_MASK_DOORBELL; arc_write(sc, ARC_RC_INTR_MASK, int_mask); error = tsleep_nsec(sc, PWAIT, "arcdb", SEC_TO_NSEC(1)); if (error == EWOULDBLOCK) { int_mask = arc_read(sc, ARC_RC_INTR_MASK) | ARC_RC_INTR_MASK_DOORBELL; arc_write(sc, ARC_RC_INTR_MASK, int_mask); } break; case ARC_HBA_TYPE_D: int_mask = arc_read(sc, ARC_RD_INTR_ENABLE) | ARC_RD_INTR_ENABLE_DOORBELL; arc_write(sc, ARC_RD_INTR_ENABLE, int_mask); error = tsleep_nsec(sc, PWAIT, "arcdb", SEC_TO_NSEC(1)); if (error == EWOULDBLOCK) { int_mask = arc_read(sc, ARC_RD_INTR_ENABLE) & ~ARC_RD_INTR_ENABLE_DOORBELL; arc_write(sc, ARC_RD_INTR_ENABLE, int_mask); } break; } splx(s); } #ifndef SMALL_KERNEL void arc_create_sensors(void *xat) { struct arc_task *at = xat; struct arc_softc *sc = at->sc; struct bioc_inq bi; struct bioc_vol bv; int i; free(at, M_TEMP, sizeof(*at)); DPRINTF("%s: arc_create_sensors\n", DEVNAME(sc)); /* * XXX * this is bollocks. the firmware has garbage coming out of it * so we have to wait a bit for it to finish spewing. */ tsleep_nsec(sc, PWAIT, "arcspew", SEC_TO_NSEC(2)); bzero(&bi, sizeof(bi)); if (arc_bio_inq(sc, &bi) != 0) { printf("%s: unable to query firmware for sensor info\n", DEVNAME(sc)); return; } sc->sc_nsensors = bi.bi_novol; sc->sc_sensors = mallocarray(sc->sc_nsensors, sizeof(struct ksensor), M_DEVBUF, M_WAITOK | M_ZERO); strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), sizeof(sc->sc_sensordev.xname)); for (i = 0; i < sc->sc_nsensors; i++) { bzero(&bv, sizeof(bv)); bv.bv_volid = i; if (arc_bio_vol(sc, &bv) != 0) { DPRINTF("%s: arc_bio_vol failed!\n", DEVNAME(sc)); goto bad; } sc->sc_sensors[i].type = SENSOR_DRIVE; sc->sc_sensors[i].status = SENSOR_S_UNKNOWN; strlcpy(sc->sc_sensors[i].desc, bv.bv_dev, sizeof(sc->sc_sensors[i].desc)); sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]); } if (sensor_task_register(sc, arc_refresh_sensors, 120) == NULL) { DPRINTF("%s: sensor_task_register failed!\n", DEVNAME(sc)); goto bad; } sensordev_install(&sc->sc_sensordev); return; bad: free(sc->sc_sensors, M_DEVBUF, sc->sc_nsensors * sizeof(struct ksensor)); } void arc_refresh_sensors(void *arg) { struct arc_softc *sc = arg; struct bioc_vol bv; int i; for (i = 0; i < sc->sc_nsensors; i++) { bzero(&bv, sizeof(bv)); bv.bv_volid = i; if (arc_bio_vol(sc, &bv)) { sc->sc_sensors[i].flags = SENSOR_FINVALID; 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: /* FALLTHROUGH */ default: sc->sc_sensors[i].value = 0; /* unknown */ sc->sc_sensors[i].status = SENSOR_S_UNKNOWN; } } } #endif /* SMALL_KERNEL */ #endif /* NBIO > 0 */ u_int32_t arc_read(struct arc_softc *sc, bus_size_t r) { u_int32_t v; bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4, BUS_SPACE_BARRIER_READ); v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, r); DNPRINTF(ARC_D_RW, "%s: arc_read 0x%lx 0x%08x\n", DEVNAME(sc), r, v); return (v); } void arc_read_region(struct arc_softc *sc, bus_size_t r, void *buf, size_t len) { bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, len, BUS_SPACE_BARRIER_READ); bus_space_read_raw_region_4(sc->sc_iot, sc->sc_ioh, r, buf, len); } void arc_write(struct arc_softc *sc, bus_size_t r, u_int32_t v) { DNPRINTF(ARC_D_RW, "%s: arc_write 0x%lx 0x%08x\n", 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); } void arc_write_region(struct arc_softc *sc, bus_size_t r, void *buf, size_t len) { bus_space_write_raw_region_4(sc->sc_iot, sc->sc_ioh, r, buf, len); bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, len, BUS_SPACE_BARRIER_WRITE); } int arc_wait_eq(struct arc_softc *sc, bus_size_t r, u_int32_t mask, u_int32_t target) { int i; DNPRINTF(ARC_D_RW, "%s: arc_wait_eq 0x%lx 0x%08x 0x%08x\n", DEVNAME(sc), r, mask, target); for (i = 0; i < 10000; i++) { if ((arc_read(sc, r) & mask) == target) return (0); delay(1000); } return (1); } int arc_wait_ne(struct arc_softc *sc, bus_size_t r, u_int32_t mask, u_int32_t target) { int i; DNPRINTF(ARC_D_RW, "%s: arc_wait_ne 0x%lx 0x%08x 0x%08x\n", DEVNAME(sc), r, mask, target); for (i = 0; i < 10000; i++) { if ((arc_read(sc, r) & mask) != target) return (0); delay(1000); } return (1); } int arc_msg0(struct arc_softc *sc, u_int32_t m) { switch(sc->sc_adp_type) { case ARC_HBA_TYPE_A: /* post message */ arc_write(sc, ARC_RA_INB_MSG0, m); /* wait for the fw to do it */ if (arc_wait_eq(sc, ARC_RA_INTRSTAT, ARC_RA_INTRSTAT_MSG0, ARC_RA_INTRSTAT_MSG0) != 0) return (1); /* ack it */ arc_write(sc, ARC_RA_INTRSTAT, ARC_RA_INTRSTAT_MSG0); break; case ARC_HBA_TYPE_C: /* post message */ arc_write(sc, ARC_RC_INB_MSGADDR0, m); arc_write(sc, ARC_RC_INB_DOORBELL, ARC_RC_D2I_MSG_CMD_DONE); /* wait for the fw to do it */ if (arc_wait_eq(sc, ARC_RC_OUTB_DOORBELL, ARC_RC_I2D_MSG_CMD_DONE, ARC_RC_I2D_MSG_CMD_DONE) != 0) return (1); /* ack it */ arc_write(sc, ARC_RC_OUTB_DOORBELL_CLR, ARC_RC_I2D_MSG_CMD_DONE_CLR); break; case ARC_HBA_TYPE_D: /* post message */ arc_write(sc, ARC_RD_INB_MSGADDR0, m); /* wait for the fw to do it */ if (arc_wait_eq(sc, ARC_RD_OUTB_DOORBELL, ARC_RD_I2D_MSG_CMD_DONE, ARC_RD_I2D_MSG_CMD_DONE) != 0) return (1); /* ack it */ arc_write(sc, ARC_RD_OUTB_DOORBELL_CLR, ARC_RD_I2D_MSG_CMD_DONE_CLR); break; } return (0); } struct arc_dmamem * arc_dmamem_alloc(struct arc_softc *sc, size_t size) { struct arc_dmamem *adm; int nsegs; adm = malloc(sizeof(*adm), M_DEVBUF, M_NOWAIT | M_ZERO); if (adm == NULL) return (NULL); adm->adm_size = size; if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &adm->adm_map) != 0) goto admfree; if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &adm->adm_seg, 1, &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0) goto destroy; if (bus_dmamem_map(sc->sc_dmat, &adm->adm_seg, nsegs, size, &adm->adm_kva, BUS_DMA_NOWAIT) != 0) goto free; if (bus_dmamap_load(sc->sc_dmat, adm->adm_map, adm->adm_kva, size, NULL, BUS_DMA_NOWAIT) != 0) goto unmap; return (adm); unmap: bus_dmamem_unmap(sc->sc_dmat, adm->adm_kva, size); free: bus_dmamem_free(sc->sc_dmat, &adm->adm_seg, 1); destroy: bus_dmamap_destroy(sc->sc_dmat, adm->adm_map); admfree: free(adm, M_DEVBUF, sizeof *adm); return (NULL); } void arc_dmamem_free(struct arc_softc *sc, struct arc_dmamem *adm) { bus_dmamap_unload(sc->sc_dmat, adm->adm_map); bus_dmamem_unmap(sc->sc_dmat, adm->adm_kva, adm->adm_size); bus_dmamem_free(sc->sc_dmat, &adm->adm_seg, 1); bus_dmamap_destroy(sc->sc_dmat, adm->adm_map); free(adm, M_DEVBUF, sizeof *adm); } int arc_alloc_ccbs(struct arc_softc *sc) { struct arc_ccb *ccb; u_int8_t *cmd; u_int32_t i, size, len; SLIST_INIT(&sc->sc_ccb_free); mtx_init(&sc->sc_ccb_mtx, IPL_BIO); size = sizeof(struct arc_ccb) * ARCMSR_MAX_CCB_COUNT; sc->sc_ccbs = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); len = ARC_IO_CMD_LEN; size = ARCMSR_MAX_CCB_COUNT * len; if(sc->sc_adp_type == ARC_HBA_TYPE_D) size += sizeof(struct arc_HBD_Msgu); sc->sc_requests = arc_dmamem_alloc(sc, size); if (sc->sc_requests == NULL) { printf("%s: unable to allocate ccb dmamem\n", DEVNAME(sc)); goto free_ccbs; } cmd = ARC_DMA_KVA(sc->sc_requests); for (i = 0; i < ARCMSR_MAX_CCB_COUNT; i++) { ccb = &sc->sc_ccbs[i]; if (bus_dmamap_create(sc->sc_dmat, MAXPHYS, ARC_SGL_MAXLEN, MAXPHYS, 0, 0, &ccb->ccb_dmamap) != 0) { printf("%s: unable to create dmamap for ccb %d\n", DEVNAME(sc), i); goto free_maps; } ccb->ccb_sc = sc; ccb->cmd_dma_offset = len * i; ccb->ccb_cmd = (struct arc_io_cmd *)&cmd[ccb->cmd_dma_offset]; ccb->ccb_cmd_post = (ARC_DMA_DVA(sc->sc_requests) + ccb->cmd_dma_offset); if ((sc->sc_adp_type != ARC_HBA_TYPE_C) && (sc->sc_adp_type != ARC_HBA_TYPE_D)) ccb->ccb_cmd_post = ccb->ccb_cmd_post >> ARC_RA_POST_QUEUE_ADDR_SHIFT; arc_put_ccb(sc, ccb); } sc->sc_ccb_phys_hi = (u_int64_t)ARC_DMA_DVA(sc->sc_requests) >> 32; if(sc->sc_adp_type == ARC_HBA_TYPE_D) { sc->postQ_buffer = ARC_DMA_DVA(sc->sc_requests) + (ARCMSR_MAX_CCB_COUNT * len); sc->doneQ_buffer = sc->postQ_buffer + (sizeof(struct InBound_SRB) * ARCMSR_MAX_HBD_POSTQUEUE); sc->pmu = (struct arc_HBD_Msgu *)&cmd[ARCMSR_MAX_CCB_COUNT * len]; sc->cmdQ_ptr_offset = ARCMSR_MAX_CCB_COUNT * len; } scsi_iopool_init(&sc->sc_iopool, sc, (void *(*)(void *))arc_get_ccb, (void (*)(void *, void *))arc_put_ccb); return (0); free_maps: while ((ccb = arc_get_ccb(sc)) != NULL) bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); arc_dmamem_free(sc, sc->sc_requests); free_ccbs: free(sc->sc_ccbs, M_DEVBUF, sizeof(struct arc_ccb) * ARCMSR_MAX_CCB_COUNT); return (1); } void arc_free_ccb_src(struct arc_softc *sc) { struct arc_ccb *ccb; while ((ccb = arc_get_ccb(sc)) != NULL) bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); arc_dmamem_free(sc, sc->sc_requests); free(sc->sc_ccbs, M_DEVBUF, 0); } struct arc_ccb * arc_get_ccb(struct arc_softc *sc) { struct arc_ccb *ccb; mtx_enter(&sc->sc_ccb_mtx); ccb = SLIST_FIRST(&sc->sc_ccb_free); if (ccb != NULL) SLIST_REMOVE_HEAD(&sc->sc_ccb_free, ccb_link); mtx_leave(&sc->sc_ccb_mtx); return (ccb); } void arc_put_ccb(struct arc_softc *sc, struct arc_ccb *ccb) { ccb->ccb_xs = NULL; bzero(ccb->ccb_cmd, ARC_IO_CMD_LEN); mtx_enter(&sc->sc_ccb_mtx); SLIST_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_link); mtx_leave(&sc->sc_ccb_mtx); }